blob: ec38dde5373c28ceb9e89b0f82d699e8b0d4cbdd [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 Feist02d2b932020-02-06 16:28:48 -080028#include <boost/asio/io_context.hpp>
James Feistb1728ca2020-04-30 15:40:55 -070029#include <boost/asio/steady_timer.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080030#include <boost/container/flat_map.hpp>
31#include <boost/container/flat_set.hpp>
James Feistf5125b02019-06-06 11:27:43 -070032#include <boost/range/iterator_range.hpp>
James Feist8c505da2020-05-28 10:06:33 -070033#include <nlohmann/json.hpp>
34#include <sdbusplus/asio/connection.hpp>
35#include <sdbusplus/asio/object_server.hpp>
36
James Feist637b3ef2019-04-15 16:35:30 -070037#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080038#include <fstream>
39#include <iostream>
James Feista465ccc2019-02-08 12:51:01 -080040#include <regex>
James Feista465ccc2019-02-08 12:51:01 -080041#include <variant>
James Feista465ccc2019-02-08 12:51:01 -080042constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
43constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070044constexpr const char* tempConfigDir = "/tmp/configuration/";
45constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
46constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080047constexpr const char* globalSchema = "global.json";
James Feist8f2710a2018-05-09 17:18:55 -070048constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist3cb5fec2018-01-23 14:41:51 -080049
James Feistf1b14142019-04-10 15:22:09 -070050constexpr const bool DEBUG = false;
51
James Feist3cb5fec2018-01-23 14:41:51 -080052struct cmp_str
53{
James Feista465ccc2019-02-08 12:51:01 -080054 bool operator()(const char* a, const char* b) const
James Feist3cb5fec2018-01-23 14:41:51 -080055 {
56 return std::strcmp(a, b) < 0;
57 }
58};
59
60// underscore T for collison with dbus c api
61enum class probe_type_codes
62{
63 FALSE_T,
64 TRUE_T,
65 AND,
66 OR,
James Feist6bd2a022018-03-13 12:30:58 -070067 FOUND,
68 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080069};
James Feista465ccc2019-02-08 12:51:01 -080070const static boost::container::flat_map<const char*, probe_type_codes, cmp_str>
James Feist3cb5fec2018-01-23 14:41:51 -080071 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
72 {"TRUE", probe_type_codes::TRUE_T},
73 {"AND", probe_type_codes::AND},
74 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070075 {"FOUND", probe_type_codes::FOUND},
76 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080077
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +020078static constexpr std::array<const char*, 6> settableInterfaces = {
79 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
James Feist68500ff2018-08-08 15:40:42 -070080using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080081 std::variant<std::vector<std::string>, std::vector<double>, std::string,
82 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
83 uint16_t, uint8_t, bool>;
James Feist3cb5fec2018-01-23 14:41:51 -080084using GetSubTreeType = std::vector<
85 std::pair<std::string,
86 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
87
88using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070089 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080090 boost::container::flat_map<
91 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070092 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080093
James Feistd58879a2019-09-11 11:26:07 -070094// store reference to all interfaces so we can destroy them later
95boost::container::flat_map<
James Feist02d2b932020-02-06 16:28:48 -080096 std::string, std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>>
James Feistd58879a2019-09-11 11:26:07 -070097 inventory;
98
James Feist3cb5fec2018-01-23 14:41:51 -080099// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -0700100std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist1df06a42019-04-11 14:23:04 -0700101static nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -0800102
James Feist02d2b932020-02-06 16:28:48 -0800103boost::asio::io_context io;
104
Johnathan Mantey2015f752019-03-26 15:22:31 -0700105const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
106const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800107
James Feist4dc617b2020-05-01 09:54:47 -0700108void registerCallback(nlohmann::json& systemConfiguration,
109 sdbusplus::asio::object_server& objServer,
110 const std::string& interfaces);
James Feist75fdeeb2018-02-20 14:26:16 -0800111
James Feistd58879a2019-09-11 11:26:07 -0700112static std::shared_ptr<sdbusplus::asio::dbus_interface>
113 createInterface(sdbusplus::asio::object_server& objServer,
114 const std::string& path, const std::string& interface,
James Feist02d2b932020-02-06 16:28:48 -0800115 const std::string& parent, bool checkNull = false)
James Feistd58879a2019-09-11 11:26:07 -0700116{
James Feist02d2b932020-02-06 16:28:48 -0800117 // on first add we have no reason to check for null before add, as there
118 // won't be any. For dynamically added interfaces, we check for null so that
119 // a constant delete/add will not create a memory leak
120
121 auto ptr = objServer.add_interface(path, interface);
122 auto& dataVector = inventory[parent];
123 if (checkNull)
124 {
125 auto it = std::find_if(dataVector.begin(), dataVector.end(),
126 [](const auto& p) { return p.expired(); });
127 if (it != dataVector.end())
128 {
129 *it = ptr;
130 return ptr;
131 }
132 }
133 dataVector.emplace_back(ptr);
134 return ptr;
James Feistd58879a2019-09-11 11:26:07 -0700135}
136
James Feistb1728ca2020-04-30 15:40:55 -0700137void getInterfaces(
138 const std::tuple<std::string, std::string, std::string>& call,
139 const std::vector<std::shared_ptr<PerformProbe>>& probeVector,
140 std::shared_ptr<PerformScan> scan, size_t retries = 5)
141{
142 if (!retries)
143 {
144 std::cerr << "retries exhausted on " << std::get<0>(call) << " "
145 << std::get<1>(call) << " " << std::get<2>(call) << "\n";
146 return;
147 }
148
149 SYSTEM_BUS->async_method_call(
150 [call, scan, probeVector, retries](
151 boost::system::error_code& errc,
152 const boost::container::flat_map<std::string, BasicVariantType>&
153 resp) {
154 if (errc)
155 {
156 std::cerr << "error calling getall on " << std::get<0>(call)
157 << " " << std::get<1>(call) << " "
158 << std::get<2>(call) << "\n";
159
160 std::shared_ptr<boost::asio::steady_timer> timer =
161 std::make_shared<boost::asio::steady_timer>(io);
162 timer->expires_after(std::chrono::seconds(2));
163
164 timer->async_wait([timer, call, scan, probeVector,
165 retries](const boost::system::error_code&) {
166 getInterfaces(call, probeVector, scan, retries - 1);
167 });
168 return;
169 }
170
171 scan->dbusProbeObjects[std::get<2>(call)].emplace_back(resp);
172 },
173 std::get<0>(call), std::get<1>(call), "org.freedesktop.DBus.Properties",
174 "GetAll", std::get<2>(call));
175
176 if constexpr (DEBUG)
177 {
178 std::cerr << __func__ << " " << __LINE__ << "\n";
179 }
180}
181
James Feist3cb5fec2018-01-23 14:41:51 -0800182// calls the mapper to find all exposed objects of an interface type
183// and creates a vector<flat_map> that contains all the key value pairs
184// getManagedObjects
James Feistb1728ca2020-04-30 15:40:55 -0700185void findDbusObjects(std::vector<std::shared_ptr<PerformProbe>>&& probeVector,
186 boost::container::flat_set<std::string>&& interfaces,
James Feist733f7652019-11-13 14:32:29 -0800187 std::shared_ptr<PerformScan> scan)
James Feist3cb5fec2018-01-23 14:41:51 -0800188{
James Feist8f2710a2018-05-09 17:18:55 -0700189
James Feist733f7652019-11-13 14:32:29 -0800190 for (const auto& [interface, _] : scan->dbusProbeObjects)
191 {
192 interfaces.erase(interface);
193 }
194 if (interfaces.empty())
195 {
196 return;
197 }
198
James Feist3cb5fec2018-01-23 14:41:51 -0800199 // find all connections in the mapper that expose a specific type
James Feistb1728ca2020-04-30 15:40:55 -0700200 SYSTEM_BUS->async_method_call(
201 [interfaces{std::move(interfaces)}, probeVector{std::move(probeVector)},
James Feist733f7652019-11-13 14:32:29 -0800202 scan](boost::system::error_code& ec,
203 const GetSubTreeType& interfaceSubtree) {
James Feistb1728ca2020-04-30 15:40:55 -0700204 boost::container::flat_set<
205 std::tuple<std::string, std::string, std::string>>
206 interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700207 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700208 {
James Feist0de40152018-07-25 11:56:12 -0700209 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700210 {
James Feist0de40152018-07-25 11:56:12 -0700211 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700212 }
James Feist0de40152018-07-25 11:56:12 -0700213 std::cerr << "Error communicating to mapper.\n";
214
215 // if we can't communicate to the mapper something is very wrong
216 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700217 }
James Feist787c3c32019-11-07 14:42:58 -0800218
James Feistb1728ca2020-04-30 15:40:55 -0700219 for (const auto& [path, object] : interfaceSubtree)
James Feist3cb5fec2018-01-23 14:41:51 -0800220 {
James Feistb1728ca2020-04-30 15:40:55 -0700221 for (const auto& [busname, ifaces] : object)
James Feist8f2710a2018-05-09 17:18:55 -0700222 {
James Feistb1728ca2020-04-30 15:40:55 -0700223 for (const std::string& iface : ifaces)
224 {
225 auto ifaceObjFind = interfaces.find(iface);
226
227 if (ifaceObjFind != interfaces.end())
228 {
229 interfaceConnections.emplace(busname, path, iface);
230 }
231 }
James Feist8f2710a2018-05-09 17:18:55 -0700232 }
James Feist3cb5fec2018-01-23 14:41:51 -0800233 }
James Feist787c3c32019-11-07 14:42:58 -0800234
James Feist63845bf2019-01-24 12:19:51 -0800235 if (interfaceConnections.empty())
236 {
James Feist63845bf2019-01-24 12:19:51 -0800237 return;
238 }
James Feist787c3c32019-11-07 14:42:58 -0800239
James Feistb1728ca2020-04-30 15:40:55 -0700240 for (const auto& call : interfaceConnections)
241 {
242 getInterfaces(call, probeVector, scan);
James Feist8f2710a2018-05-09 17:18:55 -0700243 }
244 },
245 "xyz.openbmc_project.ObjectMapper",
246 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700247 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist787c3c32019-11-07 14:42:58 -0800248 interfaces);
James Feistb1728ca2020-04-30 15:40:55 -0700249
250 if constexpr (DEBUG)
251 {
252 std::cerr << __func__ << " " << __LINE__ << "\n";
253 }
James Feist3cb5fec2018-01-23 14:41:51 -0800254}
James Feistb1728ca2020-04-30 15:40:55 -0700255
James Feist8f2710a2018-05-09 17:18:55 -0700256// probes dbus interface dictionary for a key with a value that matches a regex
James Feist08a5b172019-08-28 14:47:47 -0700257bool probeDbus(const std::string& interface,
258 const std::map<std::string, nlohmann::json>& matches,
James Feist733f7652019-11-13 14:32:29 -0800259 FoundDeviceT& devices, std::shared_ptr<PerformScan> scan,
260 bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800261{
James Feista465ccc2019-02-08 12:51:01 -0800262 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
James Feist733f7652019-11-13 14:32:29 -0800263 dbusObject = scan->dbusProbeObjects[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800264 if (dbusObject.empty())
265 {
James Feist8f2710a2018-05-09 17:18:55 -0700266 foundProbe = false;
267 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800268 }
269 foundProbe = true;
270
271 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800272 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800273 {
274 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800275 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800276 {
277 auto deviceValue = device.find(match.first);
278 if (deviceValue != device.end())
279 {
280 switch (match.second.type())
281 {
James Feist9eb0b582018-04-27 12:15:46 -0700282 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800283 {
James Feist9eb0b582018-04-27 12:15:46 -0700284 std::regex search(match.second.get<std::string>());
James Feist98132792019-07-09 13:29:09 -0700285 std::smatch regMatch;
James Feist9eb0b582018-04-27 12:15:46 -0700286
287 // convert value to string respresentation
James Feista465ccc2019-02-08 12:51:01 -0800288 std::string probeValue = std::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700289 VariantToStringVisitor(), deviceValue->second);
James Feist98132792019-07-09 13:29:09 -0700290 if (!std::regex_search(probeValue, regMatch, search))
James Feist9eb0b582018-04-27 12:15:46 -0700291 {
292 deviceMatches = false;
293 break;
294 }
James Feist3cb5fec2018-01-23 14:41:51 -0800295 break;
296 }
James Feist9eb0b582018-04-27 12:15:46 -0700297 case nlohmann::json::value_t::boolean:
298 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800299 {
James Feista465ccc2019-02-08 12:51:01 -0800300 unsigned int probeValue = std::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700301 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800302
James Feist9eb0b582018-04-27 12:15:46 -0700303 if (probeValue != match.second.get<unsigned int>())
304 {
305 deviceMatches = false;
306 }
307 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800308 }
James Feist9eb0b582018-04-27 12:15:46 -0700309 case nlohmann::json::value_t::number_integer:
310 {
James Feista465ccc2019-02-08 12:51:01 -0800311 int probeValue = std::visit(VariantToIntVisitor(),
312 deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800313
James Feist9eb0b582018-04-27 12:15:46 -0700314 if (probeValue != match.second.get<int>())
315 {
316 deviceMatches = false;
317 }
318 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800319 }
James Feist9eb0b582018-04-27 12:15:46 -0700320 case nlohmann::json::value_t::number_float:
321 {
James Feista465ccc2019-02-08 12:51:01 -0800322 float probeValue = std::visit(VariantToFloatVisitor(),
323 deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700324
325 if (probeValue != match.second.get<float>())
326 {
327 deviceMatches = false;
328 }
329 break;
330 }
James Feist0eb40352019-04-09 14:44:04 -0700331 default:
332 {
333 std::cerr << "unexpected dbus probe type "
334 << match.second.type_name() << "\n";
335 }
James Feist3cb5fec2018-01-23 14:41:51 -0800336 }
337 }
338 else
339 {
340 deviceMatches = false;
341 break;
342 }
343 }
344 if (deviceMatches)
345 {
James Feistf5125b02019-06-06 11:27:43 -0700346 devices.emplace_back(device);
James Feist3cb5fec2018-01-23 14:41:51 -0800347 foundMatch = true;
348 deviceMatches = false; // for next iteration
349 }
350 }
351 return foundMatch;
352}
353
354// default probe entry point, iterates a list looking for specific types to
355// call specific probe functions
356bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800357 const std::vector<std::string>& probeCommand,
James Feist733f7652019-11-13 14:32:29 -0800358 std::shared_ptr<PerformScan> scan,
James Feist08a5b172019-08-28 14:47:47 -0700359 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
360 foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800361{
362 const static std::regex command(R"(\((.*)\))");
363 std::smatch match;
364 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700365 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800366 bool cur = true;
367 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
James Feist54a0dca2019-06-26 10:34:54 -0700368 bool first = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800369
James Feista465ccc2019-02-08 12:51:01 -0800370 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800371 {
372 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800373 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800374 cmp_str>::const_iterator probeType;
375
376 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
Patrick Venture4b7a7452019-10-28 08:41:02 -0700377 ++probeType)
James Feist3cb5fec2018-01-23 14:41:51 -0800378 {
379 if (probe.find(probeType->first) != std::string::npos)
380 {
381 foundProbe = true;
382 break;
383 }
384 }
385 if (foundProbe)
386 {
387 switch (probeType->second)
388 {
James Feist9eb0b582018-04-27 12:15:46 -0700389 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800390 {
James Feiste31e00a2019-07-24 10:45:43 -0700391 cur = false;
392 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800393 }
James Feist9eb0b582018-04-27 12:15:46 -0700394 case probe_type_codes::TRUE_T:
395 {
James Feiste31e00a2019-07-24 10:45:43 -0700396 cur = true;
397 break;
James Feist9eb0b582018-04-27 12:15:46 -0700398 }
399 case probe_type_codes::MATCH_ONE:
400 {
401 // set current value to last, this probe type shouldn't
402 // affect the outcome
403 cur = ret;
404 matchOne = true;
405 break;
406 }
407 /*case probe_type_codes::AND:
408 break;
409 case probe_type_codes::OR:
410 break;
411 // these are no-ops until the last command switch
412 */
413 case probe_type_codes::FOUND:
414 {
415 if (!std::regex_search(probe, match, command))
416 {
James Feist0eb40352019-04-09 14:44:04 -0700417 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700418 << "\n";
419 return false;
420 }
421 std::string commandStr = *(match.begin() + 1);
422 boost::replace_all(commandStr, "'", "");
James Feist733f7652019-11-13 14:32:29 -0800423 cur = (std::find(scan->passedProbes.begin(),
424 scan->passedProbes.end(),
425 commandStr) != scan->passedProbes.end());
James Feist9eb0b582018-04-27 12:15:46 -0700426 break;
427 }
James Feist0eb40352019-04-09 14:44:04 -0700428 default:
429 {
430 break;
431 }
James Feist3cb5fec2018-01-23 14:41:51 -0800432 }
433 }
434 // look on dbus for object
435 else
436 {
437 if (!std::regex_search(probe, match, command))
438 {
James Feist0eb40352019-04-09 14:44:04 -0700439 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800440 return false;
441 }
442 std::string commandStr = *(match.begin() + 1);
443 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700444 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800445 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800446 auto json = nlohmann::json::parse(commandStr, nullptr, false);
447 if (json.is_discarded())
448 {
James Feist0eb40352019-04-09 14:44:04 -0700449 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800450 return false;
451 }
452 // we can match any (string, variant) property. (string, string)
453 // does a regex
454 std::map<std::string, nlohmann::json> dbusProbeMap =
455 json.get<std::map<std::string, nlohmann::json>>();
456 auto findStart = probe.find("(");
457 if (findStart == std::string::npos)
458 {
459 return false;
460 }
461 std::string probeInterface = probe.substr(0, findStart);
James Feist733f7652019-11-13 14:32:29 -0800462 cur = probeDbus(probeInterface, dbusProbeMap, foundDevs, scan,
463 foundProbe);
James Feist3cb5fec2018-01-23 14:41:51 -0800464 }
465
466 // some functions like AND and OR only take affect after the
467 // fact
James Feist54a0dca2019-06-26 10:34:54 -0700468 if (lastCommand == probe_type_codes::AND)
James Feist3cb5fec2018-01-23 14:41:51 -0800469 {
James Feist54a0dca2019-06-26 10:34:54 -0700470 ret = cur && ret;
471 }
472 else if (lastCommand == probe_type_codes::OR)
473 {
474 ret = cur || ret;
475 }
476
477 if (first)
478 {
479 ret = cur;
480 first = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800481 }
482 lastCommand = probeType != PROBE_TYPES.end()
483 ? probeType->second
484 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800485 }
486
487 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800488 if (ret && foundDevs.size() == 0)
489 {
James Feist08a5b172019-08-28 14:47:47 -0700490 foundDevs.emplace_back(
491 boost::container::flat_map<std::string, BasicVariantType>{});
James Feist3cb5fec2018-01-23 14:41:51 -0800492 }
James Feist0eb40352019-04-09 14:44:04 -0700493 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700494 {
James Feist71f295f2019-06-20 13:35:12 -0700495 // match the last one
496 auto last = foundDevs.back();
James Feist0eb40352019-04-09 14:44:04 -0700497 foundDevs.clear();
James Feist71f295f2019-06-20 13:35:12 -0700498
499 foundDevs.emplace_back(std::move(last));
James Feist6bd2a022018-03-13 12:30:58 -0700500 }
James Feist3cb5fec2018-01-23 14:41:51 -0800501 return ret;
502}
503
James Feist7d807752019-11-13 14:47:57 -0800504PerformProbe::PerformProbe(const std::vector<std::string>& probeCommand,
505 std::shared_ptr<PerformScan>& scanPtr,
506 std::function<void(FoundDeviceT&)>&& callback) :
507 _probeCommand(probeCommand),
508 scan(scanPtr), _callback(std::move(callback))
James Feist8c505da2020-05-28 10:06:33 -0700509{}
James Feist7d807752019-11-13 14:47:57 -0800510PerformProbe::~PerformProbe()
511{
512 FoundDeviceT foundDevs;
513 if (probe(_probeCommand, scan, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700514 {
James Feist7d807752019-11-13 14:47:57 -0800515 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700516 }
James Feist7d807752019-11-13 14:47:57 -0800517}
James Feist8f2710a2018-05-09 17:18:55 -0700518
519// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800520bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800521{
James Feist1df06a42019-04-11 14:23:04 -0700522 std::filesystem::create_directory(configurationOutDir);
523 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700524 if (!output.good())
525 {
526 return false;
527 }
James Feist1b2e2242018-01-30 13:45:19 -0800528 output << systemConfiguration.dump(4);
529 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700530 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700531}
James Feist1b2e2242018-01-30 13:45:19 -0800532
James Feist97a63f12018-05-17 13:50:57 -0700533template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800534bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
535 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700536{
537 try
538 {
539 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800540 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700541 ref = value;
542 return true;
543 }
James Feist98132792019-07-09 13:29:09 -0700544 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700545 {
546 return false;
547 }
548}
James Feistbb43d022018-06-12 15:44:33 -0700549
James Feistebcc26b2019-03-22 12:30:43 -0700550// template function to add array as dbus property
551template <typename PropertyType>
552void addArrayToDbus(const std::string& name, const nlohmann::json& array,
553 sdbusplus::asio::dbus_interface* iface,
554 sdbusplus::asio::PropertyPermission permission,
555 nlohmann::json& systemConfiguration,
556 const std::string& jsonPointerString)
557{
558 std::vector<PropertyType> values;
559 for (const auto& property : array)
560 {
561 auto ptr = property.get_ptr<const PropertyType*>();
562 if (ptr != nullptr)
563 {
564 values.emplace_back(*ptr);
565 }
566 }
567
568 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
569 {
570 iface->register_property(name, values);
571 }
572 else
573 {
574 iface->register_property(
575 name, values,
576 [&systemConfiguration,
577 jsonPointerString{std::string(jsonPointerString)}](
578 const std::vector<PropertyType>& newVal,
579 std::vector<PropertyType>& val) {
580 val = newVal;
581 if (!setJsonFromPointer(jsonPointerString, val,
582 systemConfiguration))
583 {
584 std::cerr << "error setting json field\n";
585 return -1;
586 }
587 if (!writeJsonFiles(systemConfiguration))
588 {
589 std::cerr << "error setting json file\n";
590 return -1;
591 }
592 return 1;
593 });
594 }
595}
596
James Feistbb43d022018-06-12 15:44:33 -0700597template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800598void addProperty(const std::string& propertyName, const PropertyType& value,
599 sdbusplus::asio::dbus_interface* iface,
600 nlohmann::json& systemConfiguration,
601 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700602 sdbusplus::asio::PropertyPermission permission)
603{
604 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
605 {
606 iface->register_property(propertyName, value);
607 return;
608 }
James Feist68500ff2018-08-08 15:40:42 -0700609 iface->register_property(
610 propertyName, value,
611 [&systemConfiguration,
612 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800613 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700614 val = newVal;
615 if (!setJsonFromPointer(jsonPointerString, val,
616 systemConfiguration))
617 {
618 std::cerr << "error setting json field\n";
619 return -1;
620 }
James Feistc6248a52018-08-14 10:09:45 -0700621 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700622 {
623 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700624 return -1;
625 }
626 return 1;
627 });
628}
629
630void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800631 const std::string& jsonPointerPath,
632 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
633 sdbusplus::asio::object_server& objServer,
634 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700635{
636 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
637 iface->register_method(
638 "Delete", [&objServer, &systemConfiguration, interface,
639 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700640 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700641 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700642 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700643 {
644 // this technically can't happen as the pointer is pointing to
645 // us
646 throw DBusInternalError();
647 }
648 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feistc6248a52018-08-14 10:09:45 -0700649 systemConfiguration[ptr] = nullptr;
650
James Feist02d2b932020-02-06 16:28:48 -0800651 // todo(james): dig through sdbusplus to find out why we can't
652 // delete it in a method call
653 io.post([&objServer, dbusInterface]() mutable {
654 objServer.remove_interface(dbusInterface);
655 });
656
James Feistc6248a52018-08-14 10:09:45 -0700657 if (!writeJsonFiles(systemConfiguration))
658 {
659 std::cerr << "error setting json file\n";
660 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700661 }
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,
James Feistd58879a2019-09-11 11:26:07 -0700838 sdbusplus::asio::object_server& objServer,
839 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700840{
James Feistd58879a2019-09-11 11:26:07 -0700841 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
842 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700843
844 iface->register_method(
845 "AddObject",
846 [&systemConfiguration, &objServer,
James Feistd58879a2019-09-11 11:26:07 -0700847 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
848 board](const boost::container::flat_map<std::string, JsonVariantType>&
849 data) {
James Feist68500ff2018-08-08 15:40:42 -0700850 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800851 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700852 auto findExposes = base.find("Exposes");
853
854 if (findExposes == base.end())
855 {
856 throw std::invalid_argument("Entity must have children.");
857 }
858
859 // this will throw invalid-argument to sdbusplus if invalid json
860 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800861 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700862 {
James Feista465ccc2019-02-08 12:51:01 -0800863 nlohmann::json& newJson = newData[item.first];
864 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
865 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700866 }
867
868 auto findName = newData.find("Name");
869 auto findType = newData.find("Type");
870 if (findName == newData.end() || findType == newData.end())
871 {
872 throw std::invalid_argument("AddObject missing Name or Type");
873 }
James Feista465ccc2019-02-08 12:51:01 -0800874 const std::string* type = findType->get_ptr<const std::string*>();
875 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700876 if (type == nullptr || name == nullptr)
877 {
878 throw std::invalid_argument("Type and Name must be a string.");
879 }
880
James Feist02d2b932020-02-06 16:28:48 -0800881 bool foundNull = false;
James Feist68500ff2018-08-08 15:40:42 -0700882 size_t lastIndex = 0;
883 // we add in the "exposes"
James Feist02d2b932020-02-06 16:28:48 -0800884 for (const auto& expose : *findExposes)
James Feist68500ff2018-08-08 15:40:42 -0700885 {
James Feist02d2b932020-02-06 16:28:48 -0800886 if (expose.is_null())
887 {
888 foundNull = true;
889 continue;
890 }
891
892 if (expose["Name"] == *name && expose["Type"] == *type)
James Feist68500ff2018-08-08 15:40:42 -0700893 {
894 throw std::invalid_argument(
895 "Field already in JSON, not adding");
896 }
James Feist02d2b932020-02-06 16:28:48 -0800897
898 if (foundNull)
899 {
900 continue;
901 }
902
James Feist68500ff2018-08-08 15:40:42 -0700903 lastIndex++;
904 }
905
906 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
907 *type + ".json");
908 // todo(james) we might want to also make a list of 'can add'
909 // interfaces but for now I think the assumption if there is a
910 // schema avaliable that it is allowed to update is fine
911 if (!schemaFile.good())
912 {
913 throw std::invalid_argument(
914 "No schema avaliable, cannot validate.");
915 }
916 nlohmann::json schema =
917 nlohmann::json::parse(schemaFile, nullptr, false);
918 if (schema.is_discarded())
919 {
920 std::cerr << "Schema not legal" << *type << ".json\n";
921 throw DBusInternalError();
922 }
923 if (!validateJson(schema, newData))
924 {
925 throw std::invalid_argument("Data does not match schema");
926 }
James Feist02d2b932020-02-06 16:28:48 -0800927 if (foundNull)
928 {
929 findExposes->at(lastIndex) = newData;
930 }
931 else
932 {
933 findExposes->push_back(newData);
934 }
James Feist68500ff2018-08-08 15:40:42 -0700935 if (!writeJsonFiles(systemConfiguration))
936 {
937 std::cerr << "Error writing json files\n";
938 throw DBusInternalError();
939 }
940 std::string dbusName = *name;
941
942 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700943 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistd58879a2019-09-11 11:26:07 -0700944
945 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
946 createInterface(objServer, path + "/" + dbusName,
947 "xyz.openbmc_project.Configuration." + *type,
James Feist02d2b932020-02-06 16:28:48 -0800948 board, true);
James Feist68500ff2018-08-08 15:40:42 -0700949 // permission is read-write, as since we just created it, must be
950 // runtime modifiable
951 populateInterfaceFromJson(
952 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700953 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700954 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700955 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700956 });
957 iface->initialize();
958}
959
James Feista465ccc2019-02-08 12:51:01 -0800960void postToDbus(const nlohmann::json& newConfiguration,
961 nlohmann::json& systemConfiguration,
962 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800963
James Feist1b2e2242018-01-30 13:45:19 -0800964{
James Feist97a63f12018-05-17 13:50:57 -0700965 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800966 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800967 {
James Feistf1b14142019-04-10 15:22:09 -0700968 std::string boardKey = boardPair.value()["Name"];
James Feistd58879a2019-09-11 11:26:07 -0700969 std::string boardKeyOrig = boardPair.value()["Name"];
James Feistf1b14142019-04-10 15:22:09 -0700970 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700971 // loop through newConfiguration, but use values from system
972 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700973 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700974 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800975 std::string boardType;
976 if (findBoardType != boardValues.end() &&
977 findBoardType->type() == nlohmann::json::value_t::string)
978 {
979 boardType = findBoardType->get<std::string>();
980 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700981 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800982 }
983 else
984 {
985 std::cerr << "Unable to find type for " << boardKey
986 << " reverting to Chassis.\n";
987 boardType = "Chassis";
988 }
James Feist11be6672018-04-06 14:05:32 -0700989 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800990
991 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700992 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700993 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
994 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800995
James Feistd58879a2019-09-11 11:26:07 -0700996 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
997 createInterface(objServer, boardName,
998 "xyz.openbmc_project.Inventory.Item", boardKey);
James Feist68500ff2018-08-08 15:40:42 -0700999
James Feistd58879a2019-09-11 11:26:07 -07001000 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
1001 createInterface(objServer, boardName,
1002 "xyz.openbmc_project.Inventory.Item." + boardType,
1003 boardKeyOrig);
James Feist11be6672018-04-06 14:05:32 -07001004
James Feist68500ff2018-08-08 15:40:42 -07001005 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -07001006 objServer, boardKeyOrig);
James Feist68500ff2018-08-08 15:40:42 -07001007
James Feist97a63f12018-05-17 13:50:57 -07001008 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001009 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -07001010 jsonPointerPath += "/";
1011 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -08001012 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -07001013 {
1014 if (boardField.value().type() == nlohmann::json::value_t::object)
1015 {
James Feistd58879a2019-09-11 11:26:07 -07001016 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
1017 createInterface(objServer, boardName, boardField.key(),
1018 boardKeyOrig);
1019
James Feistc6248a52018-08-14 10:09:45 -07001020 populateInterfaceFromJson(systemConfiguration,
1021 jsonPointerPath + boardField.key(),
1022 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -07001023 }
1024 }
James Feist97a63f12018-05-17 13:50:57 -07001025
James Feist1e3e6982018-08-03 16:09:28 -07001026 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -08001027 if (exposes == boardValues.end())
1028 {
1029 continue;
1030 }
James Feist97a63f12018-05-17 13:50:57 -07001031 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -07001032 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -07001033
1034 // store the board level pointer so we can modify it on the way down
1035 std::string jsonPointerPathBoard = jsonPointerPath;
1036 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -08001037 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001038 {
James Feist97a63f12018-05-17 13:50:57 -07001039 exposesIndex++;
1040 jsonPointerPath = jsonPointerPathBoard;
1041 jsonPointerPath += std::to_string(exposesIndex);
1042
James Feistd63d18a2018-07-19 15:23:45 -07001043 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001044 if (findName == item.end())
1045 {
1046 std::cerr << "cannot find name in field " << item << "\n";
1047 continue;
1048 }
James Feist1e3e6982018-08-03 16:09:28 -07001049 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001050 // if status is not found it is assumed to be status = 'okay'
1051 if (findStatus != item.end())
1052 {
1053 if (*findStatus == "disabled")
1054 {
1055 continue;
1056 }
1057 }
James Feistd63d18a2018-07-19 15:23:45 -07001058 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001059 std::string itemType;
1060 if (findType != item.end())
1061 {
1062 itemType = findType->get<std::string>();
1063 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001064 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1065 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001066 }
1067 else
1068 {
1069 itemType = "unknown";
1070 }
1071 std::string itemName = findName->get<std::string>();
1072 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001073 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001074
James Feistd58879a2019-09-11 11:26:07 -07001075 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
1076 createInterface(objServer, boardName + "/" + itemName,
1077 "xyz.openbmc_project.Configuration." + itemType,
1078 boardKeyOrig);
James Feist1b2e2242018-01-30 13:45:19 -08001079
James Feist97a63f12018-05-17 13:50:57 -07001080 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001081 itemIface, item, objServer,
1082 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001083
James Feista465ccc2019-02-08 12:51:01 -08001084 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001085 {
James Feist97a63f12018-05-17 13:50:57 -07001086 jsonPointerPath = jsonPointerPathBoard +
1087 std::to_string(exposesIndex) + "/" +
1088 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001089 if (objectPair.value().type() ==
1090 nlohmann::json::value_t::object)
1091 {
James Feistd58879a2019-09-11 11:26:07 -07001092 std::shared_ptr<sdbusplus::asio::dbus_interface>
1093 objectIface = createInterface(
1094 objServer, boardName + "/" + itemName,
1095 "xyz.openbmc_project.Configuration." + itemType +
1096 "." + objectPair.key(),
1097 boardKeyOrig);
James Feist97a63f12018-05-17 13:50:57 -07001098
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +02001099 populateInterfaceFromJson(systemConfiguration,
1100 jsonPointerPath, objectIface,
1101 objectPair.value(), objServer,
1102 getPermission(objectPair.key()));
James Feist1b2e2242018-01-30 13:45:19 -08001103 }
1104 else if (objectPair.value().type() ==
1105 nlohmann::json::value_t::array)
1106 {
1107 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001108 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001109 {
James Feist8f2710a2018-05-09 17:18:55 -07001110 continue;
1111 }
1112 bool isLegal = true;
1113 auto type = objectPair.value()[0].type();
1114 if (type != nlohmann::json::value_t::object)
1115 {
1116 continue;
1117 }
1118
1119 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001120 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001121 {
1122 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001123 {
James Feist8f2710a2018-05-09 17:18:55 -07001124 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001125 break;
1126 }
James Feist8f2710a2018-05-09 17:18:55 -07001127 }
1128 if (!isLegal)
1129 {
1130 std::cerr << "dbus format error" << objectPair.value()
1131 << "\n";
1132 break;
1133 }
1134
James Feista465ccc2019-02-08 12:51:01 -08001135 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001136 {
James Feist97a63f12018-05-17 13:50:57 -07001137
James Feistd58879a2019-09-11 11:26:07 -07001138 std::shared_ptr<sdbusplus::asio::dbus_interface>
1139 objectIface = createInterface(
1140 objServer, boardName + "/" + itemName,
1141 "xyz.openbmc_project.Configuration." +
1142 itemType + "." + objectPair.key() +
1143 std::to_string(index),
1144 boardKeyOrig);
1145
James Feistc6248a52018-08-14 10:09:45 -07001146 populateInterfaceFromJson(
1147 systemConfiguration,
1148 jsonPointerPath + "/" + std::to_string(index),
1149 objectIface, arrayItem, objServer,
1150 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001151 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001152 }
1153 }
1154 }
1155 }
1156 }
1157}
1158
James Feist8f2710a2018-05-09 17:18:55 -07001159// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001160bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001161{
1162 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001163 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001164 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1165 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001166 {
1167 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001168 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001169 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001170 }
James Feistb4383f42018-08-06 16:54:10 -07001171
1172 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1173 globalSchema);
1174 if (!schemaStream.good())
1175 {
1176 std::cerr
1177 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1178 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001179 return false;
James Feistb4383f42018-08-06 16:54:10 -07001180 }
1181 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1182 if (schema.is_discarded())
1183 {
1184 std::cerr
1185 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1186 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001187 return false;
James Feistb4383f42018-08-06 16:54:10 -07001188 }
1189
James Feista465ccc2019-02-08 12:51:01 -08001190 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001191 {
1192 std::ifstream jsonStream(jsonPath.c_str());
1193 if (!jsonStream.good())
1194 {
1195 std::cerr << "unable to open " << jsonPath.string() << "\n";
1196 continue;
1197 }
1198 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1199 if (data.is_discarded())
1200 {
1201 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1202 continue;
1203 }
James Feist8da99192019-01-24 08:20:16 -08001204 /*
1205 * todo(james): reenable this once less things are in flight
1206 *
James Feistb4383f42018-08-06 16:54:10 -07001207 if (!validateJson(schema, data))
1208 {
1209 std::cerr << "Error validating " << jsonPath.string() << "\n";
1210 continue;
1211 }
James Feist8da99192019-01-24 08:20:16 -08001212 */
James Feistb4383f42018-08-06 16:54:10 -07001213
James Feist3cb5fec2018-01-23 14:41:51 -08001214 if (data.type() == nlohmann::json::value_t::array)
1215 {
James Feista465ccc2019-02-08 12:51:01 -08001216 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001217 {
1218 configurations.emplace_back(d);
1219 }
1220 }
1221 else
1222 {
1223 configurations.emplace_back(data);
1224 }
1225 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001226 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001227}
James Feist3cb5fec2018-01-23 14:41:51 -08001228
James Feist94219d12020-03-03 11:52:25 -08001229std::string getRecordName(
1230 const boost::container::flat_map<std::string, BasicVariantType>& probe,
1231 const std::string& probeName)
1232{
1233 if (probe.empty())
1234 {
1235 return probeName;
1236 }
1237
1238 // use an array so alphabetical order from the
1239 // flat_map is maintained
1240 auto device = nlohmann::json::array();
1241 for (auto& devPair : probe)
1242 {
1243 device.push_back(devPair.first);
1244 std::visit([&device](auto&& v) { device.push_back(v); },
1245 devPair.second);
1246 }
1247 size_t hash = std::hash<std::string>{}(probeName + device.dump());
1248 // hashes are hard to distinguish, use the
1249 // non-hashed version if we want debug
1250 if constexpr (DEBUG)
1251 {
1252 return probeName + device.dump();
1253 }
1254 else
1255 {
1256 return std::to_string(hash);
1257 }
1258}
1259
James Feist4dc617b2020-05-01 09:54:47 -07001260PerformScan::PerformScan(nlohmann::json& systemConfiguration,
1261 nlohmann::json& missingConfigurations,
1262 std::list<nlohmann::json>& configurations,
1263 sdbusplus::asio::object_server& objServerIn,
1264 std::function<void()>&& callback) :
James Feist733f7652019-11-13 14:32:29 -08001265 _systemConfiguration(systemConfiguration),
1266 _missingConfigurations(missingConfigurations),
James Feist4dc617b2020-05-01 09:54:47 -07001267 _configurations(configurations), objServer(objServerIn),
1268 _callback(std::move(callback))
James Feist8c505da2020-05-28 10:06:33 -07001269{}
James Feist733f7652019-11-13 14:32:29 -08001270void PerformScan::run()
1271{
1272 boost::container::flat_set<std::string> dbusProbeInterfaces;
1273 std::vector<std::shared_ptr<PerformProbe>> dbusProbePointers;
James Feist75fdeeb2018-02-20 14:26:16 -08001274
James Feist733f7652019-11-13 14:32:29 -08001275 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001276 {
James Feist733f7652019-11-13 14:32:29 -08001277 auto findProbe = it->find("Probe");
1278 auto findName = it->find("Name");
James Feist787c3c32019-11-07 14:42:58 -08001279
James Feist733f7652019-11-13 14:32:29 -08001280 nlohmann::json probeCommand;
1281 // check for poorly formatted fields, probe must be an array
1282 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001283 {
James Feist733f7652019-11-13 14:32:29 -08001284 std::cerr << "configuration file missing probe:\n " << *it << "\n";
1285 it = _configurations.erase(it);
1286 continue;
1287 }
1288 else if ((*findProbe).type() != nlohmann::json::value_t::array)
1289 {
1290 probeCommand = nlohmann::json::array();
1291 probeCommand.push_back(*findProbe);
1292 }
1293 else
1294 {
1295 probeCommand = *findProbe;
1296 }
James Feist3cb5fec2018-01-23 14:41:51 -08001297
James Feist733f7652019-11-13 14:32:29 -08001298 if (findName == it->end())
1299 {
1300 std::cerr << "configuration file missing name:\n " << *it << "\n";
1301 it = _configurations.erase(it);
1302 continue;
1303 }
1304 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001305
James Feist733f7652019-11-13 14:32:29 -08001306 if (std::find(passedProbes.begin(), passedProbes.end(), probeName) !=
1307 passedProbes.end())
1308 {
1309 it = _configurations.erase(it);
1310 continue;
1311 }
1312 nlohmann::json* recordPtr = &(*it);
James Feist1b2e2242018-01-30 13:45:19 -08001313
James Feist733f7652019-11-13 14:32:29 -08001314 // store reference to this to children to makes sure we don't get
1315 // destroyed too early
1316 auto thisRef = shared_from_this();
1317 auto probePointer = std::make_shared<PerformProbe>(
1318 probeCommand, thisRef,
1319 [&, recordPtr, probeName](FoundDeviceT& foundDevices) {
James Feist08a5b172019-08-28 14:47:47 -07001320 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001321
James Feist35f5e0e2020-03-16 14:02:27 -07001322 std::set<nlohmann::json> usedNames;
James Feist733f7652019-11-13 14:32:29 -08001323 passedProbes.push_back(probeName);
James Feist94219d12020-03-03 11:52:25 -08001324 std::list<size_t> indexes(foundDevices.size());
1325 std::iota(indexes.begin(), indexes.end(), 1);
James Feist8f2710a2018-05-09 17:18:55 -07001326
James Feist35f5e0e2020-03-16 14:02:27 -07001327 size_t indexIdx = probeName.find("$");
1328 bool hasTemplateName = (indexIdx != std::string::npos);
James Feist94219d12020-03-03 11:52:25 -08001329
1330 // copy over persisted configurations and make sure we remove
1331 // indexes that are already used
1332 for (auto itr = foundDevices.begin();
1333 itr != foundDevices.end();)
James Feist08a5b172019-08-28 14:47:47 -07001334 {
James Feist94219d12020-03-03 11:52:25 -08001335 std::string recordName = getRecordName(*itr, probeName);
James Feistf1b14142019-04-10 15:22:09 -07001336
James Feist08a5b172019-08-28 14:47:47 -07001337 auto fromLastJson = lastJson.find(recordName);
1338 if (fromLastJson != lastJson.end())
1339 {
James Feist02d2b932020-02-06 16:28:48 -08001340 auto findExposes = fromLastJson->find("Exposes");
1341 // delete nulls from any updates
1342 if (findExposes != fromLastJson->end())
1343 {
1344 auto copy = nlohmann::json::array();
1345 for (auto& expose : *findExposes)
1346 {
1347 if (expose.is_null())
1348 {
1349 continue;
1350 }
1351 copy.emplace_back(expose);
1352 }
1353 *findExposes = copy;
1354 }
1355
James Feist08a5b172019-08-28 14:47:47 -07001356 // keep user changes
1357 _systemConfiguration[recordName] = *fromLastJson;
James Feist899e17f2019-09-13 11:46:29 -07001358 _missingConfigurations.erase(recordName);
James Feist94219d12020-03-03 11:52:25 -08001359 itr = foundDevices.erase(itr);
James Feist35f5e0e2020-03-16 14:02:27 -07001360 if (hasTemplateName)
James Feist94219d12020-03-03 11:52:25 -08001361 {
1362 auto nameIt = fromLastJson->find("Name");
1363 if (nameIt == fromLastJson->end())
1364 {
1365 std::cerr << "Last JSON Illegal\n";
1366 continue;
1367 }
1368
1369 int index = std::stoi(
1370 nameIt->get<std::string>().substr(indexIdx),
1371 nullptr, 0);
James Feist35f5e0e2020-03-16 14:02:27 -07001372 usedNames.insert(nameIt.value());
James Feist94219d12020-03-03 11:52:25 -08001373 auto usedIt = std::find(indexes.begin(),
1374 indexes.end(), index);
1375
1376 if (usedIt == indexes.end())
1377 {
1378 continue; // less items now
1379 }
1380 indexes.erase(usedIt);
1381 }
1382
James Feist08a5b172019-08-28 14:47:47 -07001383 continue;
1384 }
James Feist94219d12020-03-03 11:52:25 -08001385 itr++;
1386 }
James Feist35f5e0e2020-03-16 14:02:27 -07001387
1388 std::optional<std::string> replaceStr;
1389
James Feist94219d12020-03-03 11:52:25 -08001390 for (auto& foundDevice : foundDevices)
1391 {
1392 nlohmann::json record = *recordPtr;
1393 std::string recordName =
1394 getRecordName(foundDevice, probeName);
1395 size_t foundDeviceIdx = indexes.front();
1396 indexes.pop_front();
James Feistf1b14142019-04-10 15:22:09 -07001397
James Feist35f5e0e2020-03-16 14:02:27 -07001398 // check name first so we have no duplicate names
1399 auto getName = record.find("Name");
1400 if (getName == record.end())
1401 {
1402 std::cerr << "Record Missing Name! " << record.dump();
1403 continue; // this should be impossible at this level
1404 }
1405
1406 nlohmann::json copyForName = {{"Name", getName.value()}};
1407 nlohmann::json::iterator copyIt = copyForName.begin();
1408 std::optional<std::string> replaceVal = templateCharReplace(
1409 copyIt, foundDevice, foundDeviceIdx, replaceStr);
1410
1411 if (!replaceStr && replaceVal)
1412 {
1413 if (usedNames.find(copyIt.value()) != usedNames.end())
1414 {
1415 replaceStr = replaceVal;
1416 copyForName = {{"Name", getName.value()}};
1417 copyIt = copyForName.begin();
1418 templateCharReplace(copyIt, foundDevice,
1419 foundDeviceIdx, replaceStr);
1420 }
1421 }
1422
1423 if (replaceStr)
1424 {
1425 std::cerr << "Duplicates found, replacing "
1426 << *replaceStr
1427 << " with found device index.\n Consider "
1428 "fixing template to not have duplicates\n";
1429 }
James Feist08a5b172019-08-28 14:47:47 -07001430
1431 for (auto keyPair = record.begin(); keyPair != record.end();
1432 keyPair++)
1433 {
James Feist35f5e0e2020-03-16 14:02:27 -07001434 if (keyPair.key() == "Name")
1435 {
1436 keyPair.value() = copyIt.value();
1437 usedNames.insert(copyIt.value());
1438
1439 continue; // already covered above
1440 }
James Feist08a5b172019-08-28 14:47:47 -07001441 templateCharReplace(keyPair, foundDevice,
James Feist35f5e0e2020-03-16 14:02:27 -07001442 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001443 }
1444
James Feist35f5e0e2020-03-16 14:02:27 -07001445 // insert into configuration temporarily to be able to
1446 // reference ourselves
1447
1448 _systemConfiguration[recordName] = record;
1449
James Feist08a5b172019-08-28 14:47:47 -07001450 auto findExpose = record.find("Exposes");
1451 if (findExpose == record.end())
1452 {
James Feistf1b14142019-04-10 15:22:09 -07001453 _systemConfiguration[recordName] = record;
James Feist08a5b172019-08-28 14:47:47 -07001454 continue;
1455 }
James Feistf1b14142019-04-10 15:22:09 -07001456
James Feist08a5b172019-08-28 14:47:47 -07001457 for (auto& expose : *findExpose)
1458 {
1459 for (auto keyPair = expose.begin();
1460 keyPair != expose.end(); keyPair++)
James Feistf1b14142019-04-10 15:22:09 -07001461 {
James Feist08a5b172019-08-28 14:47:47 -07001462
1463 templateCharReplace(keyPair, foundDevice,
James Feist35f5e0e2020-03-16 14:02:27 -07001464 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001465
James Feist668bbb12020-02-05 14:27:26 -08001466 bool isBind =
1467 boost::starts_with(keyPair.key(), "Bind");
1468 bool isDisable = keyPair.key() == "DisableNode";
1469
1470 // special cases
1471 if (!(isBind || isDisable))
James Feistf1b14142019-04-10 15:22:09 -07001472 {
James Feist668bbb12020-02-05 14:27:26 -08001473 continue;
1474 }
1475
1476 if (keyPair.value().type() !=
1477 nlohmann::json::value_t::string &&
1478 keyPair.value().type() !=
1479 nlohmann::json::value_t::array)
1480 {
1481 std::cerr << "Value is invalid type "
1482 << keyPair.key() << "\n";
1483 continue;
1484 }
1485
1486 std::vector<std::string> matches;
1487 if (keyPair.value().type() ==
1488 nlohmann::json::value_t::string)
1489 {
1490 matches.emplace_back(keyPair.value());
1491 }
1492 else
1493 {
1494 for (const auto& value : keyPair.value())
James Feistf1b14142019-04-10 15:22:09 -07001495 {
James Feist668bbb12020-02-05 14:27:26 -08001496 if (value.type() !=
1497 nlohmann::json::value_t::string)
1498 {
1499 std::cerr << "Value is invalid type "
1500 << value << "\n";
1501 break;
1502 }
1503 matches.emplace_back(value);
James Feistf1b14142019-04-10 15:22:09 -07001504 }
James Feist668bbb12020-02-05 14:27:26 -08001505 }
James Feist08a5b172019-08-28 14:47:47 -07001506
James Feist668bbb12020-02-05 14:27:26 -08001507 std::set<std::string> foundMatches;
1508 for (auto& configurationPair :
1509 _systemConfiguration.items())
1510 {
1511 if (isDisable)
James Feist8f2710a2018-05-09 17:18:55 -07001512 {
James Feist668bbb12020-02-05 14:27:26 -08001513 // don't disable ourselves
1514 if (configurationPair.key() == recordName)
James Feist3cb5fec2018-01-23 14:41:51 -08001515 {
James Feist1b2e2242018-01-30 13:45:19 -08001516 continue;
1517 }
James Feist668bbb12020-02-05 14:27:26 -08001518 }
1519 auto configListFind =
1520 configurationPair.value().find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001521
James Feist668bbb12020-02-05 14:27:26 -08001522 if (configListFind ==
1523 configurationPair.value().end() ||
1524 configListFind->type() !=
1525 nlohmann::json::value_t::array)
James Feist08a5b172019-08-28 14:47:47 -07001526 {
James Feist668bbb12020-02-05 14:27:26 -08001527 continue;
James Feist08a5b172019-08-28 14:47:47 -07001528 }
James Feist668bbb12020-02-05 14:27:26 -08001529 for (auto& exposedObject : *configListFind)
1530 {
1531 auto matchIt = std::find_if(
1532 matches.begin(), matches.end(),
1533 [name = (exposedObject)["Name"]
1534 .get<std::string>()](
1535 const std::string& s) {
1536 return s == name;
1537 });
1538 if (matchIt == matches.end())
1539 {
1540 continue;
1541 }
1542 foundMatches.insert(*matchIt);
1543
1544 if (isBind)
1545 {
1546 std::string bind = keyPair.key().substr(
1547 sizeof("Bind") - 1);
1548
1549 exposedObject["Status"] = "okay";
1550 expose[bind] = exposedObject;
1551 }
1552 else if (isDisable)
1553 {
1554 exposedObject["Status"] = "disabled";
1555 }
1556 }
1557 }
1558 if (foundMatches.size() != matches.size())
1559 {
1560 std::cerr << "configuration file "
1561 "dependency error, "
1562 "could not find "
1563 << keyPair.key() << " "
1564 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001565 }
1566 }
1567 }
James Feist08a5b172019-08-28 14:47:47 -07001568 // overwrite ourselves with cleaned up version
1569 _systemConfiguration[recordName] = record;
James Feist899e17f2019-09-13 11:46:29 -07001570 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001571 }
1572 });
James Feist787c3c32019-11-07 14:42:58 -08001573
James Feist733f7652019-11-13 14:32:29 -08001574 // parse out dbus probes by discarding other probe types, store in a
1575 // map
1576 for (const std::string& probe : probeCommand)
1577 {
1578 bool found = false;
1579 boost::container::flat_map<const char*, probe_type_codes,
1580 cmp_str>::const_iterator probeType;
1581 for (probeType = PROBE_TYPES.begin();
1582 probeType != PROBE_TYPES.end(); ++probeType)
James Feist787c3c32019-11-07 14:42:58 -08001583 {
James Feist733f7652019-11-13 14:32:29 -08001584 if (probe.find(probeType->first) != std::string::npos)
James Feist787c3c32019-11-07 14:42:58 -08001585 {
James Feist733f7652019-11-13 14:32:29 -08001586 found = true;
1587 break;
James Feist787c3c32019-11-07 14:42:58 -08001588 }
James Feist787c3c32019-11-07 14:42:58 -08001589 }
James Feist733f7652019-11-13 14:32:29 -08001590 if (found)
1591 {
1592 continue;
1593 }
1594 // syntax requires probe before first open brace
1595 auto findStart = probe.find("(");
1596 std::string interface = probe.substr(0, findStart);
1597 dbusProbeInterfaces.emplace(interface);
1598 dbusProbePointers.emplace_back(probePointer);
James Feist3cb5fec2018-01-23 14:41:51 -08001599 }
James Feist733f7652019-11-13 14:32:29 -08001600 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001601 }
James Feist75fdeeb2018-02-20 14:26:16 -08001602
James Feist4dc617b2020-05-01 09:54:47 -07001603 for (const std::string& interface : dbusProbeInterfaces)
1604 {
1605 registerCallback(_systemConfiguration, objServer, interface);
1606 }
1607
James Feist733f7652019-11-13 14:32:29 -08001608 // probe vector stores a shared_ptr to each PerformProbe that cares
1609 // about a dbus interface
James Feistb1728ca2020-04-30 15:40:55 -07001610 findDbusObjects(std::move(dbusProbePointers),
1611 std::move(dbusProbeInterfaces), shared_from_this());
1612 if constexpr (DEBUG)
1613 {
1614 std::cerr << __func__ << " " << __LINE__ << "\n";
1615 }
James Feist733f7652019-11-13 14:32:29 -08001616}
1617
1618PerformScan::~PerformScan()
1619{
1620 if (_passed)
James Feist75fdeeb2018-02-20 14:26:16 -08001621 {
James Feist733f7652019-11-13 14:32:29 -08001622 auto nextScan = std::make_shared<PerformScan>(
1623 _systemConfiguration, _missingConfigurations, _configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001624 objServer, std::move(_callback));
James Feist733f7652019-11-13 14:32:29 -08001625 nextScan->passedProbes = std::move(passedProbes);
1626 nextScan->dbusProbeObjects = std::move(dbusProbeObjects);
1627 nextScan->run();
James Feistb1728ca2020-04-30 15:40:55 -07001628
1629 if constexpr (DEBUG)
1630 {
1631 std::cerr << __func__ << " " << __LINE__ << "\n";
1632 }
James Feist8f2710a2018-05-09 17:18:55 -07001633 }
James Feist733f7652019-11-13 14:32:29 -08001634 else
1635 {
James Feist4dc617b2020-05-01 09:54:47 -07001636 _callback();
James Feistb1728ca2020-04-30 15:40:55 -07001637
1638 if constexpr (DEBUG)
1639 {
1640 std::cerr << __func__ << " " << __LINE__ << "\n";
1641 }
James Feist733f7652019-11-13 14:32:29 -08001642 }
1643}
James Feistc95cb142018-02-26 10:41:42 -08001644
James Feistb1728ca2020-04-30 15:40:55 -07001645void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001646 nlohmann::json& systemConfiguration)
1647{
1648 static bool scannedPowerOff = false;
1649 static bool scannedPowerOn = false;
1650
James Feistfb00f392019-06-25 14:16:48 -07001651 if (systemConfiguration.empty() || lastJson.empty())
1652 {
1653 return; // not ready yet
1654 }
James Feist1df06a42019-04-11 14:23:04 -07001655 if (scannedPowerOn)
1656 {
1657 return;
1658 }
1659
1660 if (!isPowerOn() && scannedPowerOff)
1661 {
1662 return;
1663 }
1664
James Feistb1728ca2020-04-30 15:40:55 -07001665 timer.expires_after(std::chrono::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001666 timer.async_wait(
1667 [&systemConfiguration](const boost::system::error_code& ec) {
1668 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001669 {
James Feist1a996582019-05-14 15:10:06 -07001670 // we were cancelled
1671 return;
1672 }
1673
1674 bool powerOff = !isPowerOn();
1675 for (const auto& item : lastJson.items())
1676 {
1677 if (systemConfiguration.find(item.key()) ==
1678 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001679 {
James Feist1a996582019-05-14 15:10:06 -07001680 bool isDetectedPowerOn = false;
1681 auto powerState = item.value().find("PowerState");
1682 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001683 {
James Feist1a996582019-05-14 15:10:06 -07001684 auto ptr = powerState->get_ptr<const std::string*>();
1685 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001686 {
James Feist1a996582019-05-14 15:10:06 -07001687 if (*ptr == "On" || *ptr == "BiosPost")
1688 {
1689 isDetectedPowerOn = true;
1690 }
James Feist1df06a42019-04-11 14:23:04 -07001691 }
1692 }
James Feist1a996582019-05-14 15:10:06 -07001693 if (powerOff && isDetectedPowerOn)
1694 {
1695 // power not on yet, don't know if it's there or not
1696 continue;
1697 }
1698 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1699 {
1700 // already logged it when power was off
1701 continue;
1702 }
James Feist1df06a42019-04-11 14:23:04 -07001703
James Feist1a996582019-05-14 15:10:06 -07001704 logDeviceRemoved(item.value());
1705 }
James Feist1df06a42019-04-11 14:23:04 -07001706 }
James Feist1a996582019-05-14 15:10:06 -07001707 scannedPowerOff = true;
1708 if (!powerOff)
1709 {
1710 scannedPowerOn = true;
1711 }
1712 });
James Feist1df06a42019-04-11 14:23:04 -07001713}
1714
James Feist8f2710a2018-05-09 17:18:55 -07001715// main properties changed entry
James Feist4dc617b2020-05-01 09:54:47 -07001716void propertiesChangedCallback(nlohmann::json& systemConfiguration,
1717 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001718{
James Feist2539ccd2020-05-01 16:15:08 -07001719 static bool inProgress = false;
James Feistb1728ca2020-04-30 15:40:55 -07001720 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001721 static size_t instance = 0;
1722 instance++;
1723 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001724
James Feistb1728ca2020-04-30 15:40:55 -07001725 timer.expires_after(std::chrono::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001726
1727 // setup an async wait as we normally get flooded with new requests
James Feist4dc617b2020-05-01 09:54:47 -07001728 timer.async_wait([&systemConfiguration, &objServer,
James Feist899e17f2019-09-13 11:46:29 -07001729 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001730 if (ec == boost::asio::error::operation_aborted)
1731 {
1732 // we were cancelled
1733 return;
1734 }
1735 else if (ec)
1736 {
1737 std::cerr << "async wait error " << ec << "\n";
1738 return;
1739 }
1740
James Feist2539ccd2020-05-01 16:15:08 -07001741 if (inProgress)
1742 {
1743 propertiesChangedCallback(systemConfiguration, objServer);
1744 return;
1745 }
1746 inProgress = true;
1747
James Feist8f2710a2018-05-09 17:18:55 -07001748 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001749 auto missingConfigurations = std::make_shared<nlohmann::json>();
1750 *missingConfigurations = systemConfiguration;
1751
James Feist8f2710a2018-05-09 17:18:55 -07001752 std::list<nlohmann::json> configurations;
1753 if (!findJsonFiles(configurations))
1754 {
1755 std::cerr << "cannot find json files\n";
James Feist2539ccd2020-05-01 16:15:08 -07001756 inProgress = false;
James Feist8f2710a2018-05-09 17:18:55 -07001757 return;
1758 }
1759
1760 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001761 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001762 objServer,
1763 [&systemConfiguration, &objServer, count, oldConfiguration,
1764 missingConfigurations]() {
James Feist899e17f2019-09-13 11:46:29 -07001765 // this is something that since ac has been applied to the bmc
1766 // we saw, and we no longer see it
1767 bool powerOff = !isPowerOn();
1768 for (const auto& item : missingConfigurations->items())
1769 {
1770 bool isDetectedPowerOn = false;
1771 auto powerState = item.value().find("PowerState");
1772 if (powerState != item.value().end())
1773 {
1774 auto ptr = powerState->get_ptr<const std::string*>();
1775 if (ptr)
1776 {
1777 if (*ptr == "On" || *ptr == "BiosPost")
1778 {
1779 isDetectedPowerOn = true;
1780 }
1781 }
1782 }
1783 if (powerOff && isDetectedPowerOn)
1784 {
1785 // power not on yet, don't know if it's there or not
1786 continue;
1787 }
1788 std::string name = item.value()["Name"].get<std::string>();
James Feist02d2b932020-02-06 16:28:48 -08001789 std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
James Feist899e17f2019-09-13 11:46:29 -07001790 ifaces = inventory[name];
1791 for (auto& iface : ifaces)
1792 {
James Feist02d2b932020-02-06 16:28:48 -08001793 auto sharedPtr = iface.lock();
1794 if (!sharedPtr)
1795 {
1796 continue; // was already deleted elsewhere
1797 }
1798 objServer.remove_interface(sharedPtr);
James Feist899e17f2019-09-13 11:46:29 -07001799 }
1800 ifaces.clear();
1801 systemConfiguration.erase(item.key());
1802 logDeviceRemoved(item.value());
1803 }
1804
James Feist8f2710a2018-05-09 17:18:55 -07001805 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001806 for (auto it = newConfiguration.begin();
1807 it != newConfiguration.end();)
1808 {
1809 auto findKey = oldConfiguration.find(it.key());
1810 if (findKey != oldConfiguration.end())
1811 {
1812 it = newConfiguration.erase(it);
1813 }
1814 else
1815 {
1816 it++;
1817 }
1818 }
James Feist899e17f2019-09-13 11:46:29 -07001819 for (const auto& item : newConfiguration.items())
1820 {
1821 logDeviceAdded(item.value());
1822 }
1823
James Feist2539ccd2020-05-01 16:15:08 -07001824 inProgress = false;
1825
James Feist8f2710a2018-05-09 17:18:55 -07001826 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001827 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001828
James Feistbb43d022018-06-12 15:44:33 -07001829 io.post([&]() {
1830 if (!writeJsonFiles(systemConfiguration))
1831 {
1832 std::cerr << "Error writing json files\n";
1833 }
1834 });
James Feist8f2710a2018-05-09 17:18:55 -07001835 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001836 postToDbus(newConfiguration, systemConfiguration,
1837 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001838 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001839 {
James Feist899e17f2019-09-13 11:46:29 -07001840 return;
James Feist1df06a42019-04-11 14:23:04 -07001841 }
James Feist899e17f2019-09-13 11:46:29 -07001842 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001843 });
1844 });
1845 });
1846 perfScan->run();
1847 });
James Feist75fdeeb2018-02-20 14:26:16 -08001848}
1849
James Feist4dc617b2020-05-01 09:54:47 -07001850void registerCallback(nlohmann::json& systemConfiguration,
1851 sdbusplus::asio::object_server& objServer,
1852 const std::string& interface)
James Feist75fdeeb2018-02-20 14:26:16 -08001853{
James Feist4dc617b2020-05-01 09:54:47 -07001854 static boost::container::flat_map<std::string, sdbusplus::bus::match::match>
1855 dbusMatches;
James Feist75fdeeb2018-02-20 14:26:16 -08001856
James Feist4dc617b2020-05-01 09:54:47 -07001857 auto find = dbusMatches.find(interface);
1858 if (find != dbusMatches.end())
James Feist75fdeeb2018-02-20 14:26:16 -08001859 {
James Feist4dc617b2020-05-01 09:54:47 -07001860 return;
James Feist75fdeeb2018-02-20 14:26:16 -08001861 }
James Feist4dc617b2020-05-01 09:54:47 -07001862 std::function<void(sdbusplus::message::message & message)> eventHandler =
1863
1864 [&](sdbusplus::message::message&) {
1865 propertiesChangedCallback(systemConfiguration, objServer);
1866 };
1867
1868 sdbusplus::bus::match::match match(
1869 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
1870 "type='signal',member='PropertiesChanged',arg0='" + interface + "'",
1871 eventHandler);
1872 dbusMatches.emplace(interface, std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001873}
1874
James Feist98132792019-07-09 13:29:09 -07001875int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001876{
1877 // setup connection to dbus
James Feist8f2710a2018-05-09 17:18:55 -07001878 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001879 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001880
James Feist8f2710a2018-05-09 17:18:55 -07001881 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001882
James Feist8f2710a2018-05-09 17:18:55 -07001883 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1884 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1885 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001886
James Feist4131aea2018-03-09 09:47:30 -08001887 // to keep reference to the match / filter objects so they don't get
1888 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001889
1890 nlohmann::json systemConfiguration = nlohmann::json::object();
1891
James Feist4dc617b2020-05-01 09:54:47 -07001892 io.post(
1893 [&]() { propertiesChangedCallback(systemConfiguration, objServer); });
James Feist4131aea2018-03-09 09:47:30 -08001894
James Feistfd1264a2018-05-03 12:10:00 -07001895 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -07001896 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001897 });
James Feist8f2710a2018-05-09 17:18:55 -07001898 entityIface->initialize();
1899
James Feist1df06a42019-04-11 14:23:04 -07001900 if (fwVersionIsSame())
1901 {
1902 if (std::filesystem::is_regular_file(currentConfiguration))
1903 {
1904 // this file could just be deleted, but it's nice for debug
1905 std::filesystem::create_directory(tempConfigDir);
1906 std::filesystem::remove(lastConfiguration);
1907 std::filesystem::copy(currentConfiguration, lastConfiguration);
1908 std::filesystem::remove(currentConfiguration);
1909
1910 std::ifstream jsonStream(lastConfiguration);
1911 if (jsonStream.good())
1912 {
1913 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1914 if (data.is_discarded())
1915 {
1916 std::cerr << "syntax error in " << lastConfiguration
1917 << "\n";
1918 }
1919 else
1920 {
1921 lastJson = std::move(data);
1922 }
1923 }
1924 else
1925 {
1926 std::cerr << "unable to open " << lastConfiguration << "\n";
1927 }
1928 }
1929 }
1930 else
1931 {
1932 // not an error, just logging at this level to make it in the journal
1933 std::cerr << "Clearing previous configuration\n";
1934 std::filesystem::remove(currentConfiguration);
1935 }
1936
1937 // some boards only show up after power is on, we want to not say they are
1938 // removed until the same state happens
1939 setupPowerMatch(SYSTEM_BUS);
1940
James Feist1b2e2242018-01-30 13:45:19 -08001941 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001942
1943 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001944}