blob: 3b2b41692a787138a79bdff35eebe6d7eeaf6e8c [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
James Feist41334262019-03-25 13:30:20 -070078static constexpr std::array<const char*, 5> settableInterfaces = {
79 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds"};
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
1099 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001100 systemConfiguration, jsonPointerPath, objectIface,
1101 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001102 }
1103 else if (objectPair.value().type() ==
1104 nlohmann::json::value_t::array)
1105 {
1106 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001107 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001108 {
James Feist8f2710a2018-05-09 17:18:55 -07001109 continue;
1110 }
1111 bool isLegal = true;
1112 auto type = objectPair.value()[0].type();
1113 if (type != nlohmann::json::value_t::object)
1114 {
1115 continue;
1116 }
1117
1118 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001119 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001120 {
1121 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001122 {
James Feist8f2710a2018-05-09 17:18:55 -07001123 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001124 break;
1125 }
James Feist8f2710a2018-05-09 17:18:55 -07001126 }
1127 if (!isLegal)
1128 {
1129 std::cerr << "dbus format error" << objectPair.value()
1130 << "\n";
1131 break;
1132 }
1133
James Feista465ccc2019-02-08 12:51:01 -08001134 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001135 {
James Feist97a63f12018-05-17 13:50:57 -07001136
James Feistd58879a2019-09-11 11:26:07 -07001137 std::shared_ptr<sdbusplus::asio::dbus_interface>
1138 objectIface = createInterface(
1139 objServer, boardName + "/" + itemName,
1140 "xyz.openbmc_project.Configuration." +
1141 itemType + "." + objectPair.key() +
1142 std::to_string(index),
1143 boardKeyOrig);
1144
James Feistc6248a52018-08-14 10:09:45 -07001145 populateInterfaceFromJson(
1146 systemConfiguration,
1147 jsonPointerPath + "/" + std::to_string(index),
1148 objectIface, arrayItem, objServer,
1149 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001150 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001151 }
1152 }
1153 }
1154 }
1155 }
1156}
1157
James Feist8f2710a2018-05-09 17:18:55 -07001158// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001159bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001160{
1161 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001162 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001163 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1164 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001165 {
1166 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001167 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001168 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001169 }
James Feistb4383f42018-08-06 16:54:10 -07001170
1171 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1172 globalSchema);
1173 if (!schemaStream.good())
1174 {
1175 std::cerr
1176 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1177 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001178 return false;
James Feistb4383f42018-08-06 16:54:10 -07001179 }
1180 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1181 if (schema.is_discarded())
1182 {
1183 std::cerr
1184 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1185 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001186 return false;
James Feistb4383f42018-08-06 16:54:10 -07001187 }
1188
James Feista465ccc2019-02-08 12:51:01 -08001189 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001190 {
1191 std::ifstream jsonStream(jsonPath.c_str());
1192 if (!jsonStream.good())
1193 {
1194 std::cerr << "unable to open " << jsonPath.string() << "\n";
1195 continue;
1196 }
1197 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1198 if (data.is_discarded())
1199 {
1200 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1201 continue;
1202 }
James Feist8da99192019-01-24 08:20:16 -08001203 /*
1204 * todo(james): reenable this once less things are in flight
1205 *
James Feistb4383f42018-08-06 16:54:10 -07001206 if (!validateJson(schema, data))
1207 {
1208 std::cerr << "Error validating " << jsonPath.string() << "\n";
1209 continue;
1210 }
James Feist8da99192019-01-24 08:20:16 -08001211 */
James Feistb4383f42018-08-06 16:54:10 -07001212
James Feist3cb5fec2018-01-23 14:41:51 -08001213 if (data.type() == nlohmann::json::value_t::array)
1214 {
James Feista465ccc2019-02-08 12:51:01 -08001215 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001216 {
1217 configurations.emplace_back(d);
1218 }
1219 }
1220 else
1221 {
1222 configurations.emplace_back(data);
1223 }
1224 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001225 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001226}
James Feist3cb5fec2018-01-23 14:41:51 -08001227
James Feist94219d12020-03-03 11:52:25 -08001228std::string getRecordName(
1229 const boost::container::flat_map<std::string, BasicVariantType>& probe,
1230 const std::string& probeName)
1231{
1232 if (probe.empty())
1233 {
1234 return probeName;
1235 }
1236
1237 // use an array so alphabetical order from the
1238 // flat_map is maintained
1239 auto device = nlohmann::json::array();
1240 for (auto& devPair : probe)
1241 {
1242 device.push_back(devPair.first);
1243 std::visit([&device](auto&& v) { device.push_back(v); },
1244 devPair.second);
1245 }
1246 size_t hash = std::hash<std::string>{}(probeName + device.dump());
1247 // hashes are hard to distinguish, use the
1248 // non-hashed version if we want debug
1249 if constexpr (DEBUG)
1250 {
1251 return probeName + device.dump();
1252 }
1253 else
1254 {
1255 return std::to_string(hash);
1256 }
1257}
1258
James Feist4dc617b2020-05-01 09:54:47 -07001259PerformScan::PerformScan(nlohmann::json& systemConfiguration,
1260 nlohmann::json& missingConfigurations,
1261 std::list<nlohmann::json>& configurations,
1262 sdbusplus::asio::object_server& objServerIn,
1263 std::function<void()>&& callback) :
James Feist733f7652019-11-13 14:32:29 -08001264 _systemConfiguration(systemConfiguration),
1265 _missingConfigurations(missingConfigurations),
James Feist4dc617b2020-05-01 09:54:47 -07001266 _configurations(configurations), objServer(objServerIn),
1267 _callback(std::move(callback))
James Feist8c505da2020-05-28 10:06:33 -07001268{}
James Feist733f7652019-11-13 14:32:29 -08001269void PerformScan::run()
1270{
1271 boost::container::flat_set<std::string> dbusProbeInterfaces;
1272 std::vector<std::shared_ptr<PerformProbe>> dbusProbePointers;
James Feist75fdeeb2018-02-20 14:26:16 -08001273
James Feist733f7652019-11-13 14:32:29 -08001274 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001275 {
James Feist733f7652019-11-13 14:32:29 -08001276 auto findProbe = it->find("Probe");
1277 auto findName = it->find("Name");
James Feist787c3c32019-11-07 14:42:58 -08001278
James Feist733f7652019-11-13 14:32:29 -08001279 nlohmann::json probeCommand;
1280 // check for poorly formatted fields, probe must be an array
1281 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001282 {
James Feist733f7652019-11-13 14:32:29 -08001283 std::cerr << "configuration file missing probe:\n " << *it << "\n";
1284 it = _configurations.erase(it);
1285 continue;
1286 }
1287 else if ((*findProbe).type() != nlohmann::json::value_t::array)
1288 {
1289 probeCommand = nlohmann::json::array();
1290 probeCommand.push_back(*findProbe);
1291 }
1292 else
1293 {
1294 probeCommand = *findProbe;
1295 }
James Feist3cb5fec2018-01-23 14:41:51 -08001296
James Feist733f7652019-11-13 14:32:29 -08001297 if (findName == it->end())
1298 {
1299 std::cerr << "configuration file missing name:\n " << *it << "\n";
1300 it = _configurations.erase(it);
1301 continue;
1302 }
1303 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001304
James Feist733f7652019-11-13 14:32:29 -08001305 if (std::find(passedProbes.begin(), passedProbes.end(), probeName) !=
1306 passedProbes.end())
1307 {
1308 it = _configurations.erase(it);
1309 continue;
1310 }
1311 nlohmann::json* recordPtr = &(*it);
James Feist1b2e2242018-01-30 13:45:19 -08001312
James Feist733f7652019-11-13 14:32:29 -08001313 // store reference to this to children to makes sure we don't get
1314 // destroyed too early
1315 auto thisRef = shared_from_this();
1316 auto probePointer = std::make_shared<PerformProbe>(
1317 probeCommand, thisRef,
1318 [&, recordPtr, probeName](FoundDeviceT& foundDevices) {
James Feist08a5b172019-08-28 14:47:47 -07001319 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001320
James Feist35f5e0e2020-03-16 14:02:27 -07001321 std::set<nlohmann::json> usedNames;
James Feist733f7652019-11-13 14:32:29 -08001322 passedProbes.push_back(probeName);
James Feist94219d12020-03-03 11:52:25 -08001323 std::list<size_t> indexes(foundDevices.size());
1324 std::iota(indexes.begin(), indexes.end(), 1);
James Feist8f2710a2018-05-09 17:18:55 -07001325
James Feist35f5e0e2020-03-16 14:02:27 -07001326 size_t indexIdx = probeName.find("$");
1327 bool hasTemplateName = (indexIdx != std::string::npos);
James Feist94219d12020-03-03 11:52:25 -08001328
1329 // copy over persisted configurations and make sure we remove
1330 // indexes that are already used
1331 for (auto itr = foundDevices.begin();
1332 itr != foundDevices.end();)
James Feist08a5b172019-08-28 14:47:47 -07001333 {
James Feist94219d12020-03-03 11:52:25 -08001334 std::string recordName = getRecordName(*itr, probeName);
James Feistf1b14142019-04-10 15:22:09 -07001335
James Feist08a5b172019-08-28 14:47:47 -07001336 auto fromLastJson = lastJson.find(recordName);
1337 if (fromLastJson != lastJson.end())
1338 {
James Feist02d2b932020-02-06 16:28:48 -08001339 auto findExposes = fromLastJson->find("Exposes");
1340 // delete nulls from any updates
1341 if (findExposes != fromLastJson->end())
1342 {
1343 auto copy = nlohmann::json::array();
1344 for (auto& expose : *findExposes)
1345 {
1346 if (expose.is_null())
1347 {
1348 continue;
1349 }
1350 copy.emplace_back(expose);
1351 }
1352 *findExposes = copy;
1353 }
1354
James Feist08a5b172019-08-28 14:47:47 -07001355 // keep user changes
1356 _systemConfiguration[recordName] = *fromLastJson;
James Feist899e17f2019-09-13 11:46:29 -07001357 _missingConfigurations.erase(recordName);
James Feist94219d12020-03-03 11:52:25 -08001358 itr = foundDevices.erase(itr);
James Feist35f5e0e2020-03-16 14:02:27 -07001359 if (hasTemplateName)
James Feist94219d12020-03-03 11:52:25 -08001360 {
1361 auto nameIt = fromLastJson->find("Name");
1362 if (nameIt == fromLastJson->end())
1363 {
1364 std::cerr << "Last JSON Illegal\n";
1365 continue;
1366 }
1367
1368 int index = std::stoi(
1369 nameIt->get<std::string>().substr(indexIdx),
1370 nullptr, 0);
James Feist35f5e0e2020-03-16 14:02:27 -07001371 usedNames.insert(nameIt.value());
James Feist94219d12020-03-03 11:52:25 -08001372 auto usedIt = std::find(indexes.begin(),
1373 indexes.end(), index);
1374
1375 if (usedIt == indexes.end())
1376 {
1377 continue; // less items now
1378 }
1379 indexes.erase(usedIt);
1380 }
1381
James Feist08a5b172019-08-28 14:47:47 -07001382 continue;
1383 }
James Feist94219d12020-03-03 11:52:25 -08001384 itr++;
1385 }
James Feist35f5e0e2020-03-16 14:02:27 -07001386
1387 std::optional<std::string> replaceStr;
1388
James Feist94219d12020-03-03 11:52:25 -08001389 for (auto& foundDevice : foundDevices)
1390 {
1391 nlohmann::json record = *recordPtr;
1392 std::string recordName =
1393 getRecordName(foundDevice, probeName);
1394 size_t foundDeviceIdx = indexes.front();
1395 indexes.pop_front();
James Feistf1b14142019-04-10 15:22:09 -07001396
James Feist35f5e0e2020-03-16 14:02:27 -07001397 // check name first so we have no duplicate names
1398 auto getName = record.find("Name");
1399 if (getName == record.end())
1400 {
1401 std::cerr << "Record Missing Name! " << record.dump();
1402 continue; // this should be impossible at this level
1403 }
1404
1405 nlohmann::json copyForName = {{"Name", getName.value()}};
1406 nlohmann::json::iterator copyIt = copyForName.begin();
1407 std::optional<std::string> replaceVal = templateCharReplace(
1408 copyIt, foundDevice, foundDeviceIdx, replaceStr);
1409
1410 if (!replaceStr && replaceVal)
1411 {
1412 if (usedNames.find(copyIt.value()) != usedNames.end())
1413 {
1414 replaceStr = replaceVal;
1415 copyForName = {{"Name", getName.value()}};
1416 copyIt = copyForName.begin();
1417 templateCharReplace(copyIt, foundDevice,
1418 foundDeviceIdx, replaceStr);
1419 }
1420 }
1421
1422 if (replaceStr)
1423 {
1424 std::cerr << "Duplicates found, replacing "
1425 << *replaceStr
1426 << " with found device index.\n Consider "
1427 "fixing template to not have duplicates\n";
1428 }
James Feist08a5b172019-08-28 14:47:47 -07001429
1430 for (auto keyPair = record.begin(); keyPair != record.end();
1431 keyPair++)
1432 {
James Feist35f5e0e2020-03-16 14:02:27 -07001433 if (keyPair.key() == "Name")
1434 {
1435 keyPair.value() = copyIt.value();
1436 usedNames.insert(copyIt.value());
1437
1438 continue; // already covered above
1439 }
James Feist08a5b172019-08-28 14:47:47 -07001440 templateCharReplace(keyPair, foundDevice,
James Feist35f5e0e2020-03-16 14:02:27 -07001441 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001442 }
1443
James Feist35f5e0e2020-03-16 14:02:27 -07001444 // insert into configuration temporarily to be able to
1445 // reference ourselves
1446
1447 _systemConfiguration[recordName] = record;
1448
James Feist08a5b172019-08-28 14:47:47 -07001449 auto findExpose = record.find("Exposes");
1450 if (findExpose == record.end())
1451 {
James Feistf1b14142019-04-10 15:22:09 -07001452 _systemConfiguration[recordName] = record;
James Feist08a5b172019-08-28 14:47:47 -07001453 continue;
1454 }
James Feistf1b14142019-04-10 15:22:09 -07001455
James Feist08a5b172019-08-28 14:47:47 -07001456 for (auto& expose : *findExpose)
1457 {
1458 for (auto keyPair = expose.begin();
1459 keyPair != expose.end(); keyPair++)
James Feistf1b14142019-04-10 15:22:09 -07001460 {
James Feist08a5b172019-08-28 14:47:47 -07001461
1462 templateCharReplace(keyPair, foundDevice,
James Feist35f5e0e2020-03-16 14:02:27 -07001463 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001464
James Feist668bbb12020-02-05 14:27:26 -08001465 bool isBind =
1466 boost::starts_with(keyPair.key(), "Bind");
1467 bool isDisable = keyPair.key() == "DisableNode";
1468
1469 // special cases
1470 if (!(isBind || isDisable))
James Feistf1b14142019-04-10 15:22:09 -07001471 {
James Feist668bbb12020-02-05 14:27:26 -08001472 continue;
1473 }
1474
1475 if (keyPair.value().type() !=
1476 nlohmann::json::value_t::string &&
1477 keyPair.value().type() !=
1478 nlohmann::json::value_t::array)
1479 {
1480 std::cerr << "Value is invalid type "
1481 << keyPair.key() << "\n";
1482 continue;
1483 }
1484
1485 std::vector<std::string> matches;
1486 if (keyPair.value().type() ==
1487 nlohmann::json::value_t::string)
1488 {
1489 matches.emplace_back(keyPair.value());
1490 }
1491 else
1492 {
1493 for (const auto& value : keyPair.value())
James Feistf1b14142019-04-10 15:22:09 -07001494 {
James Feist668bbb12020-02-05 14:27:26 -08001495 if (value.type() !=
1496 nlohmann::json::value_t::string)
1497 {
1498 std::cerr << "Value is invalid type "
1499 << value << "\n";
1500 break;
1501 }
1502 matches.emplace_back(value);
James Feistf1b14142019-04-10 15:22:09 -07001503 }
James Feist668bbb12020-02-05 14:27:26 -08001504 }
James Feist08a5b172019-08-28 14:47:47 -07001505
James Feist668bbb12020-02-05 14:27:26 -08001506 std::set<std::string> foundMatches;
1507 for (auto& configurationPair :
1508 _systemConfiguration.items())
1509 {
1510 if (isDisable)
James Feist8f2710a2018-05-09 17:18:55 -07001511 {
James Feist668bbb12020-02-05 14:27:26 -08001512 // don't disable ourselves
1513 if (configurationPair.key() == recordName)
James Feist3cb5fec2018-01-23 14:41:51 -08001514 {
James Feist1b2e2242018-01-30 13:45:19 -08001515 continue;
1516 }
James Feist668bbb12020-02-05 14:27:26 -08001517 }
1518 auto configListFind =
1519 configurationPair.value().find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001520
James Feist668bbb12020-02-05 14:27:26 -08001521 if (configListFind ==
1522 configurationPair.value().end() ||
1523 configListFind->type() !=
1524 nlohmann::json::value_t::array)
James Feist08a5b172019-08-28 14:47:47 -07001525 {
James Feist668bbb12020-02-05 14:27:26 -08001526 continue;
James Feist08a5b172019-08-28 14:47:47 -07001527 }
James Feist668bbb12020-02-05 14:27:26 -08001528 for (auto& exposedObject : *configListFind)
1529 {
1530 auto matchIt = std::find_if(
1531 matches.begin(), matches.end(),
1532 [name = (exposedObject)["Name"]
1533 .get<std::string>()](
1534 const std::string& s) {
1535 return s == name;
1536 });
1537 if (matchIt == matches.end())
1538 {
1539 continue;
1540 }
1541 foundMatches.insert(*matchIt);
1542
1543 if (isBind)
1544 {
1545 std::string bind = keyPair.key().substr(
1546 sizeof("Bind") - 1);
1547
1548 exposedObject["Status"] = "okay";
1549 expose[bind] = exposedObject;
1550 }
1551 else if (isDisable)
1552 {
1553 exposedObject["Status"] = "disabled";
1554 }
1555 }
1556 }
1557 if (foundMatches.size() != matches.size())
1558 {
1559 std::cerr << "configuration file "
1560 "dependency error, "
1561 "could not find "
1562 << keyPair.key() << " "
1563 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001564 }
1565 }
1566 }
James Feist08a5b172019-08-28 14:47:47 -07001567 // overwrite ourselves with cleaned up version
1568 _systemConfiguration[recordName] = record;
James Feist899e17f2019-09-13 11:46:29 -07001569 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001570 }
1571 });
James Feist787c3c32019-11-07 14:42:58 -08001572
James Feist733f7652019-11-13 14:32:29 -08001573 // parse out dbus probes by discarding other probe types, store in a
1574 // map
1575 for (const std::string& probe : probeCommand)
1576 {
1577 bool found = false;
1578 boost::container::flat_map<const char*, probe_type_codes,
1579 cmp_str>::const_iterator probeType;
1580 for (probeType = PROBE_TYPES.begin();
1581 probeType != PROBE_TYPES.end(); ++probeType)
James Feist787c3c32019-11-07 14:42:58 -08001582 {
James Feist733f7652019-11-13 14:32:29 -08001583 if (probe.find(probeType->first) != std::string::npos)
James Feist787c3c32019-11-07 14:42:58 -08001584 {
James Feist733f7652019-11-13 14:32:29 -08001585 found = true;
1586 break;
James Feist787c3c32019-11-07 14:42:58 -08001587 }
James Feist787c3c32019-11-07 14:42:58 -08001588 }
James Feist733f7652019-11-13 14:32:29 -08001589 if (found)
1590 {
1591 continue;
1592 }
1593 // syntax requires probe before first open brace
1594 auto findStart = probe.find("(");
1595 std::string interface = probe.substr(0, findStart);
1596 dbusProbeInterfaces.emplace(interface);
1597 dbusProbePointers.emplace_back(probePointer);
James Feist3cb5fec2018-01-23 14:41:51 -08001598 }
James Feist733f7652019-11-13 14:32:29 -08001599 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001600 }
James Feist75fdeeb2018-02-20 14:26:16 -08001601
James Feist4dc617b2020-05-01 09:54:47 -07001602 for (const std::string& interface : dbusProbeInterfaces)
1603 {
1604 registerCallback(_systemConfiguration, objServer, interface);
1605 }
1606
James Feist733f7652019-11-13 14:32:29 -08001607 // probe vector stores a shared_ptr to each PerformProbe that cares
1608 // about a dbus interface
James Feistb1728ca2020-04-30 15:40:55 -07001609 findDbusObjects(std::move(dbusProbePointers),
1610 std::move(dbusProbeInterfaces), shared_from_this());
1611 if constexpr (DEBUG)
1612 {
1613 std::cerr << __func__ << " " << __LINE__ << "\n";
1614 }
James Feist733f7652019-11-13 14:32:29 -08001615}
1616
1617PerformScan::~PerformScan()
1618{
1619 if (_passed)
James Feist75fdeeb2018-02-20 14:26:16 -08001620 {
James Feist733f7652019-11-13 14:32:29 -08001621 auto nextScan = std::make_shared<PerformScan>(
1622 _systemConfiguration, _missingConfigurations, _configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001623 objServer, std::move(_callback));
James Feist733f7652019-11-13 14:32:29 -08001624 nextScan->passedProbes = std::move(passedProbes);
1625 nextScan->dbusProbeObjects = std::move(dbusProbeObjects);
1626 nextScan->run();
James Feistb1728ca2020-04-30 15:40:55 -07001627
1628 if constexpr (DEBUG)
1629 {
1630 std::cerr << __func__ << " " << __LINE__ << "\n";
1631 }
James Feist8f2710a2018-05-09 17:18:55 -07001632 }
James Feist733f7652019-11-13 14:32:29 -08001633 else
1634 {
James Feist4dc617b2020-05-01 09:54:47 -07001635 _callback();
James Feistb1728ca2020-04-30 15:40:55 -07001636
1637 if constexpr (DEBUG)
1638 {
1639 std::cerr << __func__ << " " << __LINE__ << "\n";
1640 }
James Feist733f7652019-11-13 14:32:29 -08001641 }
1642}
James Feistc95cb142018-02-26 10:41:42 -08001643
James Feistb1728ca2020-04-30 15:40:55 -07001644void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001645 nlohmann::json& systemConfiguration)
1646{
1647 static bool scannedPowerOff = false;
1648 static bool scannedPowerOn = false;
1649
James Feistfb00f392019-06-25 14:16:48 -07001650 if (systemConfiguration.empty() || lastJson.empty())
1651 {
1652 return; // not ready yet
1653 }
James Feist1df06a42019-04-11 14:23:04 -07001654 if (scannedPowerOn)
1655 {
1656 return;
1657 }
1658
1659 if (!isPowerOn() && scannedPowerOff)
1660 {
1661 return;
1662 }
1663
James Feistb1728ca2020-04-30 15:40:55 -07001664 timer.expires_after(std::chrono::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001665 timer.async_wait(
1666 [&systemConfiguration](const boost::system::error_code& ec) {
1667 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001668 {
James Feist1a996582019-05-14 15:10:06 -07001669 // we were cancelled
1670 return;
1671 }
1672
1673 bool powerOff = !isPowerOn();
1674 for (const auto& item : lastJson.items())
1675 {
1676 if (systemConfiguration.find(item.key()) ==
1677 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001678 {
James Feist1a996582019-05-14 15:10:06 -07001679 bool isDetectedPowerOn = false;
1680 auto powerState = item.value().find("PowerState");
1681 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001682 {
James Feist1a996582019-05-14 15:10:06 -07001683 auto ptr = powerState->get_ptr<const std::string*>();
1684 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001685 {
James Feist1a996582019-05-14 15:10:06 -07001686 if (*ptr == "On" || *ptr == "BiosPost")
1687 {
1688 isDetectedPowerOn = true;
1689 }
James Feist1df06a42019-04-11 14:23:04 -07001690 }
1691 }
James Feist1a996582019-05-14 15:10:06 -07001692 if (powerOff && isDetectedPowerOn)
1693 {
1694 // power not on yet, don't know if it's there or not
1695 continue;
1696 }
1697 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1698 {
1699 // already logged it when power was off
1700 continue;
1701 }
James Feist1df06a42019-04-11 14:23:04 -07001702
James Feist1a996582019-05-14 15:10:06 -07001703 logDeviceRemoved(item.value());
1704 }
James Feist1df06a42019-04-11 14:23:04 -07001705 }
James Feist1a996582019-05-14 15:10:06 -07001706 scannedPowerOff = true;
1707 if (!powerOff)
1708 {
1709 scannedPowerOn = true;
1710 }
1711 });
James Feist1df06a42019-04-11 14:23:04 -07001712}
1713
James Feist8f2710a2018-05-09 17:18:55 -07001714// main properties changed entry
James Feist4dc617b2020-05-01 09:54:47 -07001715void propertiesChangedCallback(nlohmann::json& systemConfiguration,
1716 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001717{
James Feist2539ccd2020-05-01 16:15:08 -07001718 static bool inProgress = false;
James Feistb1728ca2020-04-30 15:40:55 -07001719 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001720 static size_t instance = 0;
1721 instance++;
1722 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001723
James Feistb1728ca2020-04-30 15:40:55 -07001724 timer.expires_after(std::chrono::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001725
1726 // setup an async wait as we normally get flooded with new requests
James Feist4dc617b2020-05-01 09:54:47 -07001727 timer.async_wait([&systemConfiguration, &objServer,
James Feist899e17f2019-09-13 11:46:29 -07001728 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001729 if (ec == boost::asio::error::operation_aborted)
1730 {
1731 // we were cancelled
1732 return;
1733 }
1734 else if (ec)
1735 {
1736 std::cerr << "async wait error " << ec << "\n";
1737 return;
1738 }
1739
James Feist2539ccd2020-05-01 16:15:08 -07001740 if (inProgress)
1741 {
1742 propertiesChangedCallback(systemConfiguration, objServer);
1743 return;
1744 }
1745 inProgress = true;
1746
James Feist8f2710a2018-05-09 17:18:55 -07001747 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001748 auto missingConfigurations = std::make_shared<nlohmann::json>();
1749 *missingConfigurations = systemConfiguration;
1750
James Feist8f2710a2018-05-09 17:18:55 -07001751 std::list<nlohmann::json> configurations;
1752 if (!findJsonFiles(configurations))
1753 {
1754 std::cerr << "cannot find json files\n";
James Feist2539ccd2020-05-01 16:15:08 -07001755 inProgress = false;
James Feist8f2710a2018-05-09 17:18:55 -07001756 return;
1757 }
1758
1759 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001760 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001761 objServer,
1762 [&systemConfiguration, &objServer, count, oldConfiguration,
1763 missingConfigurations]() {
James Feist899e17f2019-09-13 11:46:29 -07001764 // this is something that since ac has been applied to the bmc
1765 // we saw, and we no longer see it
1766 bool powerOff = !isPowerOn();
1767 for (const auto& item : missingConfigurations->items())
1768 {
1769 bool isDetectedPowerOn = false;
1770 auto powerState = item.value().find("PowerState");
1771 if (powerState != item.value().end())
1772 {
1773 auto ptr = powerState->get_ptr<const std::string*>();
1774 if (ptr)
1775 {
1776 if (*ptr == "On" || *ptr == "BiosPost")
1777 {
1778 isDetectedPowerOn = true;
1779 }
1780 }
1781 }
1782 if (powerOff && isDetectedPowerOn)
1783 {
1784 // power not on yet, don't know if it's there or not
1785 continue;
1786 }
1787 std::string name = item.value()["Name"].get<std::string>();
James Feist02d2b932020-02-06 16:28:48 -08001788 std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
James Feist899e17f2019-09-13 11:46:29 -07001789 ifaces = inventory[name];
1790 for (auto& iface : ifaces)
1791 {
James Feist02d2b932020-02-06 16:28:48 -08001792 auto sharedPtr = iface.lock();
1793 if (!sharedPtr)
1794 {
1795 continue; // was already deleted elsewhere
1796 }
1797 objServer.remove_interface(sharedPtr);
James Feist899e17f2019-09-13 11:46:29 -07001798 }
1799 ifaces.clear();
1800 systemConfiguration.erase(item.key());
1801 logDeviceRemoved(item.value());
1802 }
1803
James Feist8f2710a2018-05-09 17:18:55 -07001804 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001805 for (auto it = newConfiguration.begin();
1806 it != newConfiguration.end();)
1807 {
1808 auto findKey = oldConfiguration.find(it.key());
1809 if (findKey != oldConfiguration.end())
1810 {
1811 it = newConfiguration.erase(it);
1812 }
1813 else
1814 {
1815 it++;
1816 }
1817 }
James Feist899e17f2019-09-13 11:46:29 -07001818 for (const auto& item : newConfiguration.items())
1819 {
1820 logDeviceAdded(item.value());
1821 }
1822
James Feist2539ccd2020-05-01 16:15:08 -07001823 inProgress = false;
1824
James Feist8f2710a2018-05-09 17:18:55 -07001825 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001826 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001827
James Feistbb43d022018-06-12 15:44:33 -07001828 io.post([&]() {
1829 if (!writeJsonFiles(systemConfiguration))
1830 {
1831 std::cerr << "Error writing json files\n";
1832 }
1833 });
James Feist8f2710a2018-05-09 17:18:55 -07001834 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001835 postToDbus(newConfiguration, systemConfiguration,
1836 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001837 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001838 {
James Feist899e17f2019-09-13 11:46:29 -07001839 return;
James Feist1df06a42019-04-11 14:23:04 -07001840 }
James Feist899e17f2019-09-13 11:46:29 -07001841 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001842 });
1843 });
1844 });
1845 perfScan->run();
1846 });
James Feist75fdeeb2018-02-20 14:26:16 -08001847}
1848
James Feist4dc617b2020-05-01 09:54:47 -07001849void registerCallback(nlohmann::json& systemConfiguration,
1850 sdbusplus::asio::object_server& objServer,
1851 const std::string& interface)
James Feist75fdeeb2018-02-20 14:26:16 -08001852{
James Feist4dc617b2020-05-01 09:54:47 -07001853 static boost::container::flat_map<std::string, sdbusplus::bus::match::match>
1854 dbusMatches;
James Feist75fdeeb2018-02-20 14:26:16 -08001855
James Feist4dc617b2020-05-01 09:54:47 -07001856 auto find = dbusMatches.find(interface);
1857 if (find != dbusMatches.end())
James Feist75fdeeb2018-02-20 14:26:16 -08001858 {
James Feist4dc617b2020-05-01 09:54:47 -07001859 return;
James Feist75fdeeb2018-02-20 14:26:16 -08001860 }
James Feist4dc617b2020-05-01 09:54:47 -07001861 std::function<void(sdbusplus::message::message & message)> eventHandler =
1862
1863 [&](sdbusplus::message::message&) {
1864 propertiesChangedCallback(systemConfiguration, objServer);
1865 };
1866
1867 sdbusplus::bus::match::match match(
1868 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
1869 "type='signal',member='PropertiesChanged',arg0='" + interface + "'",
1870 eventHandler);
1871 dbusMatches.emplace(interface, std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001872}
1873
James Feist98132792019-07-09 13:29:09 -07001874int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001875{
1876 // setup connection to dbus
James Feist8f2710a2018-05-09 17:18:55 -07001877 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001878 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001879
James Feist8f2710a2018-05-09 17:18:55 -07001880 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001881
James Feist8f2710a2018-05-09 17:18:55 -07001882 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1883 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1884 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001885
James Feist4131aea2018-03-09 09:47:30 -08001886 // to keep reference to the match / filter objects so they don't get
1887 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001888
1889 nlohmann::json systemConfiguration = nlohmann::json::object();
1890
James Feist4dc617b2020-05-01 09:54:47 -07001891 io.post(
1892 [&]() { propertiesChangedCallback(systemConfiguration, objServer); });
James Feist4131aea2018-03-09 09:47:30 -08001893
James Feistfd1264a2018-05-03 12:10:00 -07001894 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -07001895 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001896 });
James Feist8f2710a2018-05-09 17:18:55 -07001897 entityIface->initialize();
1898
James Feist1df06a42019-04-11 14:23:04 -07001899 if (fwVersionIsSame())
1900 {
1901 if (std::filesystem::is_regular_file(currentConfiguration))
1902 {
1903 // this file could just be deleted, but it's nice for debug
1904 std::filesystem::create_directory(tempConfigDir);
1905 std::filesystem::remove(lastConfiguration);
1906 std::filesystem::copy(currentConfiguration, lastConfiguration);
1907 std::filesystem::remove(currentConfiguration);
1908
1909 std::ifstream jsonStream(lastConfiguration);
1910 if (jsonStream.good())
1911 {
1912 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1913 if (data.is_discarded())
1914 {
1915 std::cerr << "syntax error in " << lastConfiguration
1916 << "\n";
1917 }
1918 else
1919 {
1920 lastJson = std::move(data);
1921 }
1922 }
1923 else
1924 {
1925 std::cerr << "unable to open " << lastConfiguration << "\n";
1926 }
1927 }
1928 }
1929 else
1930 {
1931 // not an error, just logging at this level to make it in the journal
1932 std::cerr << "Clearing previous configuration\n";
1933 std::filesystem::remove(currentConfiguration);
1934 }
1935
1936 // some boards only show up after power is on, we want to not say they are
1937 // removed until the same state happens
1938 setupPowerMatch(SYSTEM_BUS);
1939
James Feist1b2e2242018-01-30 13:45:19 -08001940 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001941
1942 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001943}