blob: af9a766af3bdb326e3edb987ce83369ba293168f [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 Feist637b3ef2019-04-15 16:35:30 -070033#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080034#include <fstream>
35#include <iostream>
36#include <nlohmann/json.hpp>
37#include <regex>
38#include <sdbusplus/asio/connection.hpp>
39#include <sdbusplus/asio/object_server.hpp>
40#include <variant>
James Feista465ccc2019-02-08 12:51:01 -080041constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
42constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070043constexpr const char* tempConfigDir = "/tmp/configuration/";
44constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
45constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080046constexpr const char* globalSchema = "global.json";
James Feist8f2710a2018-05-09 17:18:55 -070047constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist3cb5fec2018-01-23 14:41:51 -080048
James Feistf1b14142019-04-10 15:22:09 -070049constexpr const bool DEBUG = false;
50
James Feist3cb5fec2018-01-23 14:41:51 -080051struct cmp_str
52{
James Feista465ccc2019-02-08 12:51:01 -080053 bool operator()(const char* a, const char* b) const
James Feist3cb5fec2018-01-23 14:41:51 -080054 {
55 return std::strcmp(a, b) < 0;
56 }
57};
58
59// underscore T for collison with dbus c api
60enum class probe_type_codes
61{
62 FALSE_T,
63 TRUE_T,
64 AND,
65 OR,
James Feist6bd2a022018-03-13 12:30:58 -070066 FOUND,
67 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080068};
James Feista465ccc2019-02-08 12:51:01 -080069const static boost::container::flat_map<const char*, probe_type_codes, cmp_str>
James Feist3cb5fec2018-01-23 14:41:51 -080070 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
71 {"TRUE", probe_type_codes::TRUE_T},
72 {"AND", probe_type_codes::AND},
73 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070074 {"FOUND", probe_type_codes::FOUND},
75 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080076
James Feist41334262019-03-25 13:30:20 -070077static constexpr std::array<const char*, 5> settableInterfaces = {
78 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds"};
James Feist68500ff2018-08-08 15:40:42 -070079using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080080 std::variant<std::vector<std::string>, std::vector<double>, std::string,
81 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
82 uint16_t, uint8_t, bool>;
James Feist3cb5fec2018-01-23 14:41:51 -080083using GetSubTreeType = std::vector<
84 std::pair<std::string,
85 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
86
87using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070088 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080089 boost::container::flat_map<
90 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070091 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080092
James Feistd58879a2019-09-11 11:26:07 -070093// store reference to all interfaces so we can destroy them later
94boost::container::flat_map<
James Feist02d2b932020-02-06 16:28:48 -080095 std::string, std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>>
James Feistd58879a2019-09-11 11:26:07 -070096 inventory;
97
James Feist3cb5fec2018-01-23 14:41:51 -080098// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -070099std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist1df06a42019-04-11 14:23:04 -0700100static nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -0800101
James Feist02d2b932020-02-06 16:28:48 -0800102boost::asio::io_context io;
103
Johnathan Mantey2015f752019-03-26 15:22:31 -0700104const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
105const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800106
James Feist4dc617b2020-05-01 09:54:47 -0700107void registerCallback(nlohmann::json& systemConfiguration,
108 sdbusplus::asio::object_server& objServer,
109 const std::string& interfaces);
James Feist75fdeeb2018-02-20 14:26:16 -0800110
James Feistd58879a2019-09-11 11:26:07 -0700111static std::shared_ptr<sdbusplus::asio::dbus_interface>
112 createInterface(sdbusplus::asio::object_server& objServer,
113 const std::string& path, const std::string& interface,
James Feist02d2b932020-02-06 16:28:48 -0800114 const std::string& parent, bool checkNull = false)
James Feistd58879a2019-09-11 11:26:07 -0700115{
James Feist02d2b932020-02-06 16:28:48 -0800116 // on first add we have no reason to check for null before add, as there
117 // won't be any. For dynamically added interfaces, we check for null so that
118 // a constant delete/add will not create a memory leak
119
120 auto ptr = objServer.add_interface(path, interface);
121 auto& dataVector = inventory[parent];
122 if (checkNull)
123 {
124 auto it = std::find_if(dataVector.begin(), dataVector.end(),
125 [](const auto& p) { return p.expired(); });
126 if (it != dataVector.end())
127 {
128 *it = ptr;
129 return ptr;
130 }
131 }
132 dataVector.emplace_back(ptr);
133 return ptr;
James Feistd58879a2019-09-11 11:26:07 -0700134}
135
James Feistb1728ca2020-04-30 15:40:55 -0700136void getInterfaces(
137 const std::tuple<std::string, std::string, std::string>& call,
138 const std::vector<std::shared_ptr<PerformProbe>>& probeVector,
139 std::shared_ptr<PerformScan> scan, size_t retries = 5)
140{
141 if (!retries)
142 {
143 std::cerr << "retries exhausted on " << std::get<0>(call) << " "
144 << std::get<1>(call) << " " << std::get<2>(call) << "\n";
145 return;
146 }
147
148 SYSTEM_BUS->async_method_call(
149 [call, scan, probeVector, retries](
150 boost::system::error_code& errc,
151 const boost::container::flat_map<std::string, BasicVariantType>&
152 resp) {
153 if (errc)
154 {
155 std::cerr << "error calling getall on " << std::get<0>(call)
156 << " " << std::get<1>(call) << " "
157 << std::get<2>(call) << "\n";
158
159 std::shared_ptr<boost::asio::steady_timer> timer =
160 std::make_shared<boost::asio::steady_timer>(io);
161 timer->expires_after(std::chrono::seconds(2));
162
163 timer->async_wait([timer, call, scan, probeVector,
164 retries](const boost::system::error_code&) {
165 getInterfaces(call, probeVector, scan, retries - 1);
166 });
167 return;
168 }
169
170 scan->dbusProbeObjects[std::get<2>(call)].emplace_back(resp);
171 },
172 std::get<0>(call), std::get<1>(call), "org.freedesktop.DBus.Properties",
173 "GetAll", std::get<2>(call));
174
175 if constexpr (DEBUG)
176 {
177 std::cerr << __func__ << " " << __LINE__ << "\n";
178 }
179}
180
James Feist3cb5fec2018-01-23 14:41:51 -0800181// calls the mapper to find all exposed objects of an interface type
182// and creates a vector<flat_map> that contains all the key value pairs
183// getManagedObjects
James Feistb1728ca2020-04-30 15:40:55 -0700184void findDbusObjects(std::vector<std::shared_ptr<PerformProbe>>&& probeVector,
185 boost::container::flat_set<std::string>&& interfaces,
James Feist733f7652019-11-13 14:32:29 -0800186 std::shared_ptr<PerformScan> scan)
James Feist3cb5fec2018-01-23 14:41:51 -0800187{
James Feist8f2710a2018-05-09 17:18:55 -0700188
James Feist733f7652019-11-13 14:32:29 -0800189 for (const auto& [interface, _] : scan->dbusProbeObjects)
190 {
191 interfaces.erase(interface);
192 }
193 if (interfaces.empty())
194 {
195 return;
196 }
197
James Feist3cb5fec2018-01-23 14:41:51 -0800198 // find all connections in the mapper that expose a specific type
James Feistb1728ca2020-04-30 15:40:55 -0700199 SYSTEM_BUS->async_method_call(
200 [interfaces{std::move(interfaces)}, probeVector{std::move(probeVector)},
James Feist733f7652019-11-13 14:32:29 -0800201 scan](boost::system::error_code& ec,
202 const GetSubTreeType& interfaceSubtree) {
James Feistb1728ca2020-04-30 15:40:55 -0700203 boost::container::flat_set<
204 std::tuple<std::string, std::string, std::string>>
205 interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700206 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700207 {
James Feist0de40152018-07-25 11:56:12 -0700208 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700209 {
James Feist0de40152018-07-25 11:56:12 -0700210 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700211 }
James Feist0de40152018-07-25 11:56:12 -0700212 std::cerr << "Error communicating to mapper.\n";
213
214 // if we can't communicate to the mapper something is very wrong
215 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700216 }
James Feist787c3c32019-11-07 14:42:58 -0800217
James Feistb1728ca2020-04-30 15:40:55 -0700218 for (const auto& [path, object] : interfaceSubtree)
James Feist3cb5fec2018-01-23 14:41:51 -0800219 {
James Feistb1728ca2020-04-30 15:40:55 -0700220 for (const auto& [busname, ifaces] : object)
James Feist8f2710a2018-05-09 17:18:55 -0700221 {
James Feistb1728ca2020-04-30 15:40:55 -0700222 for (const std::string& iface : ifaces)
223 {
224 auto ifaceObjFind = interfaces.find(iface);
225
226 if (ifaceObjFind != interfaces.end())
227 {
228 interfaceConnections.emplace(busname, path, iface);
229 }
230 }
James Feist8f2710a2018-05-09 17:18:55 -0700231 }
James Feist3cb5fec2018-01-23 14:41:51 -0800232 }
James Feist787c3c32019-11-07 14:42:58 -0800233
James Feist63845bf2019-01-24 12:19:51 -0800234 if (interfaceConnections.empty())
235 {
James Feist63845bf2019-01-24 12:19:51 -0800236 return;
237 }
James Feist787c3c32019-11-07 14:42:58 -0800238
James Feistb1728ca2020-04-30 15:40:55 -0700239 for (const auto& call : interfaceConnections)
240 {
241 getInterfaces(call, probeVector, scan);
James Feist8f2710a2018-05-09 17:18:55 -0700242 }
243 },
244 "xyz.openbmc_project.ObjectMapper",
245 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700246 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist787c3c32019-11-07 14:42:58 -0800247 interfaces);
James Feistb1728ca2020-04-30 15:40:55 -0700248
249 if constexpr (DEBUG)
250 {
251 std::cerr << __func__ << " " << __LINE__ << "\n";
252 }
James Feist3cb5fec2018-01-23 14:41:51 -0800253}
James Feistb1728ca2020-04-30 15:40:55 -0700254
James Feist8f2710a2018-05-09 17:18:55 -0700255// probes dbus interface dictionary for a key with a value that matches a regex
James Feist08a5b172019-08-28 14:47:47 -0700256bool probeDbus(const std::string& interface,
257 const std::map<std::string, nlohmann::json>& matches,
James Feist733f7652019-11-13 14:32:29 -0800258 FoundDeviceT& devices, std::shared_ptr<PerformScan> scan,
259 bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800260{
James Feista465ccc2019-02-08 12:51:01 -0800261 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
James Feist733f7652019-11-13 14:32:29 -0800262 dbusObject = scan->dbusProbeObjects[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800263 if (dbusObject.empty())
264 {
James Feist8f2710a2018-05-09 17:18:55 -0700265 foundProbe = false;
266 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800267 }
268 foundProbe = true;
269
270 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800271 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800272 {
273 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800274 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800275 {
276 auto deviceValue = device.find(match.first);
277 if (deviceValue != device.end())
278 {
279 switch (match.second.type())
280 {
James Feist9eb0b582018-04-27 12:15:46 -0700281 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800282 {
James Feist9eb0b582018-04-27 12:15:46 -0700283 std::regex search(match.second.get<std::string>());
James Feist98132792019-07-09 13:29:09 -0700284 std::smatch regMatch;
James Feist9eb0b582018-04-27 12:15:46 -0700285
286 // convert value to string respresentation
James Feista465ccc2019-02-08 12:51:01 -0800287 std::string probeValue = std::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700288 VariantToStringVisitor(), deviceValue->second);
James Feist98132792019-07-09 13:29:09 -0700289 if (!std::regex_search(probeValue, regMatch, search))
James Feist9eb0b582018-04-27 12:15:46 -0700290 {
291 deviceMatches = false;
292 break;
293 }
James Feist3cb5fec2018-01-23 14:41:51 -0800294 break;
295 }
James Feist9eb0b582018-04-27 12:15:46 -0700296 case nlohmann::json::value_t::boolean:
297 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800298 {
James Feista465ccc2019-02-08 12:51:01 -0800299 unsigned int probeValue = std::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700300 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800301
James Feist9eb0b582018-04-27 12:15:46 -0700302 if (probeValue != match.second.get<unsigned int>())
303 {
304 deviceMatches = false;
305 }
306 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800307 }
James Feist9eb0b582018-04-27 12:15:46 -0700308 case nlohmann::json::value_t::number_integer:
309 {
James Feista465ccc2019-02-08 12:51:01 -0800310 int probeValue = std::visit(VariantToIntVisitor(),
311 deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800312
James Feist9eb0b582018-04-27 12:15:46 -0700313 if (probeValue != match.second.get<int>())
314 {
315 deviceMatches = false;
316 }
317 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800318 }
James Feist9eb0b582018-04-27 12:15:46 -0700319 case nlohmann::json::value_t::number_float:
320 {
James Feista465ccc2019-02-08 12:51:01 -0800321 float probeValue = std::visit(VariantToFloatVisitor(),
322 deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700323
324 if (probeValue != match.second.get<float>())
325 {
326 deviceMatches = false;
327 }
328 break;
329 }
James Feist0eb40352019-04-09 14:44:04 -0700330 default:
331 {
332 std::cerr << "unexpected dbus probe type "
333 << match.second.type_name() << "\n";
334 }
James Feist3cb5fec2018-01-23 14:41:51 -0800335 }
336 }
337 else
338 {
339 deviceMatches = false;
340 break;
341 }
342 }
343 if (deviceMatches)
344 {
James Feistf5125b02019-06-06 11:27:43 -0700345 devices.emplace_back(device);
James Feist3cb5fec2018-01-23 14:41:51 -0800346 foundMatch = true;
347 deviceMatches = false; // for next iteration
348 }
349 }
350 return foundMatch;
351}
352
353// default probe entry point, iterates a list looking for specific types to
354// call specific probe functions
355bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800356 const std::vector<std::string>& probeCommand,
James Feist733f7652019-11-13 14:32:29 -0800357 std::shared_ptr<PerformScan> scan,
James Feist08a5b172019-08-28 14:47:47 -0700358 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
359 foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800360{
361 const static std::regex command(R"(\((.*)\))");
362 std::smatch match;
363 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700364 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800365 bool cur = true;
366 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
James Feist54a0dca2019-06-26 10:34:54 -0700367 bool first = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800368
James Feista465ccc2019-02-08 12:51:01 -0800369 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800370 {
371 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800372 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800373 cmp_str>::const_iterator probeType;
374
375 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
Patrick Venture4b7a7452019-10-28 08:41:02 -0700376 ++probeType)
James Feist3cb5fec2018-01-23 14:41:51 -0800377 {
378 if (probe.find(probeType->first) != std::string::npos)
379 {
380 foundProbe = true;
381 break;
382 }
383 }
384 if (foundProbe)
385 {
386 switch (probeType->second)
387 {
James Feist9eb0b582018-04-27 12:15:46 -0700388 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800389 {
James Feiste31e00a2019-07-24 10:45:43 -0700390 cur = false;
391 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800392 }
James Feist9eb0b582018-04-27 12:15:46 -0700393 case probe_type_codes::TRUE_T:
394 {
James Feiste31e00a2019-07-24 10:45:43 -0700395 cur = true;
396 break;
James Feist9eb0b582018-04-27 12:15:46 -0700397 }
398 case probe_type_codes::MATCH_ONE:
399 {
400 // set current value to last, this probe type shouldn't
401 // affect the outcome
402 cur = ret;
403 matchOne = true;
404 break;
405 }
406 /*case probe_type_codes::AND:
407 break;
408 case probe_type_codes::OR:
409 break;
410 // these are no-ops until the last command switch
411 */
412 case probe_type_codes::FOUND:
413 {
414 if (!std::regex_search(probe, match, command))
415 {
James Feist0eb40352019-04-09 14:44:04 -0700416 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700417 << "\n";
418 return false;
419 }
420 std::string commandStr = *(match.begin() + 1);
421 boost::replace_all(commandStr, "'", "");
James Feist733f7652019-11-13 14:32:29 -0800422 cur = (std::find(scan->passedProbes.begin(),
423 scan->passedProbes.end(),
424 commandStr) != scan->passedProbes.end());
James Feist9eb0b582018-04-27 12:15:46 -0700425 break;
426 }
James Feist0eb40352019-04-09 14:44:04 -0700427 default:
428 {
429 break;
430 }
James Feist3cb5fec2018-01-23 14:41:51 -0800431 }
432 }
433 // look on dbus for object
434 else
435 {
436 if (!std::regex_search(probe, match, command))
437 {
James Feist0eb40352019-04-09 14:44:04 -0700438 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800439 return false;
440 }
441 std::string commandStr = *(match.begin() + 1);
442 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700443 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800444 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800445 auto json = nlohmann::json::parse(commandStr, nullptr, false);
446 if (json.is_discarded())
447 {
James Feist0eb40352019-04-09 14:44:04 -0700448 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800449 return false;
450 }
451 // we can match any (string, variant) property. (string, string)
452 // does a regex
453 std::map<std::string, nlohmann::json> dbusProbeMap =
454 json.get<std::map<std::string, nlohmann::json>>();
455 auto findStart = probe.find("(");
456 if (findStart == std::string::npos)
457 {
458 return false;
459 }
460 std::string probeInterface = probe.substr(0, findStart);
James Feist733f7652019-11-13 14:32:29 -0800461 cur = probeDbus(probeInterface, dbusProbeMap, foundDevs, scan,
462 foundProbe);
James Feist3cb5fec2018-01-23 14:41:51 -0800463 }
464
465 // some functions like AND and OR only take affect after the
466 // fact
James Feist54a0dca2019-06-26 10:34:54 -0700467 if (lastCommand == probe_type_codes::AND)
James Feist3cb5fec2018-01-23 14:41:51 -0800468 {
James Feist54a0dca2019-06-26 10:34:54 -0700469 ret = cur && ret;
470 }
471 else if (lastCommand == probe_type_codes::OR)
472 {
473 ret = cur || ret;
474 }
475
476 if (first)
477 {
478 ret = cur;
479 first = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800480 }
481 lastCommand = probeType != PROBE_TYPES.end()
482 ? probeType->second
483 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800484 }
485
486 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800487 if (ret && foundDevs.size() == 0)
488 {
James Feist08a5b172019-08-28 14:47:47 -0700489 foundDevs.emplace_back(
490 boost::container::flat_map<std::string, BasicVariantType>{});
James Feist3cb5fec2018-01-23 14:41:51 -0800491 }
James Feist0eb40352019-04-09 14:44:04 -0700492 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700493 {
James Feist71f295f2019-06-20 13:35:12 -0700494 // match the last one
495 auto last = foundDevs.back();
James Feist0eb40352019-04-09 14:44:04 -0700496 foundDevs.clear();
James Feist71f295f2019-06-20 13:35:12 -0700497
498 foundDevs.emplace_back(std::move(last));
James Feist6bd2a022018-03-13 12:30:58 -0700499 }
James Feist3cb5fec2018-01-23 14:41:51 -0800500 return ret;
501}
502
James Feist7d807752019-11-13 14:47:57 -0800503PerformProbe::PerformProbe(const std::vector<std::string>& probeCommand,
504 std::shared_ptr<PerformScan>& scanPtr,
505 std::function<void(FoundDeviceT&)>&& callback) :
506 _probeCommand(probeCommand),
507 scan(scanPtr), _callback(std::move(callback))
508{
509}
510PerformProbe::~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 Feist75fdeeb2018-02-20 14:26:16 -08001268{
James Feist733f7652019-11-13 14:32:29 -08001269}
1270void 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 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
1740 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001741 auto missingConfigurations = std::make_shared<nlohmann::json>();
1742 *missingConfigurations = systemConfiguration;
1743
James Feist8f2710a2018-05-09 17:18:55 -07001744 std::list<nlohmann::json> configurations;
1745 if (!findJsonFiles(configurations))
1746 {
1747 std::cerr << "cannot find json files\n";
1748 return;
1749 }
1750
1751 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001752 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001753 objServer,
1754 [&systemConfiguration, &objServer, count, oldConfiguration,
1755 missingConfigurations]() {
James Feist899e17f2019-09-13 11:46:29 -07001756 // this is something that since ac has been applied to the bmc
1757 // we saw, and we no longer see it
1758 bool powerOff = !isPowerOn();
1759 for (const auto& item : missingConfigurations->items())
1760 {
1761 bool isDetectedPowerOn = false;
1762 auto powerState = item.value().find("PowerState");
1763 if (powerState != item.value().end())
1764 {
1765 auto ptr = powerState->get_ptr<const std::string*>();
1766 if (ptr)
1767 {
1768 if (*ptr == "On" || *ptr == "BiosPost")
1769 {
1770 isDetectedPowerOn = true;
1771 }
1772 }
1773 }
1774 if (powerOff && isDetectedPowerOn)
1775 {
1776 // power not on yet, don't know if it's there or not
1777 continue;
1778 }
1779 std::string name = item.value()["Name"].get<std::string>();
James Feist02d2b932020-02-06 16:28:48 -08001780 std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
James Feist899e17f2019-09-13 11:46:29 -07001781 ifaces = inventory[name];
1782 for (auto& iface : ifaces)
1783 {
James Feist02d2b932020-02-06 16:28:48 -08001784 auto sharedPtr = iface.lock();
1785 if (!sharedPtr)
1786 {
1787 continue; // was already deleted elsewhere
1788 }
1789 objServer.remove_interface(sharedPtr);
James Feist899e17f2019-09-13 11:46:29 -07001790 }
1791 ifaces.clear();
1792 systemConfiguration.erase(item.key());
1793 logDeviceRemoved(item.value());
1794 }
1795
James Feist8f2710a2018-05-09 17:18:55 -07001796 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001797 for (auto it = newConfiguration.begin();
1798 it != newConfiguration.end();)
1799 {
1800 auto findKey = oldConfiguration.find(it.key());
1801 if (findKey != oldConfiguration.end())
1802 {
1803 it = newConfiguration.erase(it);
1804 }
1805 else
1806 {
1807 it++;
1808 }
1809 }
James Feist899e17f2019-09-13 11:46:29 -07001810 for (const auto& item : newConfiguration.items())
1811 {
1812 logDeviceAdded(item.value());
1813 }
1814
James Feist8f2710a2018-05-09 17:18:55 -07001815 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001816 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001817
James Feistbb43d022018-06-12 15:44:33 -07001818 io.post([&]() {
1819 if (!writeJsonFiles(systemConfiguration))
1820 {
1821 std::cerr << "Error writing json files\n";
1822 }
1823 });
James Feist8f2710a2018-05-09 17:18:55 -07001824 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001825 postToDbus(newConfiguration, systemConfiguration,
1826 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001827 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001828 {
James Feist899e17f2019-09-13 11:46:29 -07001829 return;
James Feist1df06a42019-04-11 14:23:04 -07001830 }
James Feist899e17f2019-09-13 11:46:29 -07001831 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001832 });
1833 });
1834 });
1835 perfScan->run();
1836 });
James Feist75fdeeb2018-02-20 14:26:16 -08001837}
1838
James Feist4dc617b2020-05-01 09:54:47 -07001839void registerCallback(nlohmann::json& systemConfiguration,
1840 sdbusplus::asio::object_server& objServer,
1841 const std::string& interface)
James Feist75fdeeb2018-02-20 14:26:16 -08001842{
James Feist4dc617b2020-05-01 09:54:47 -07001843 static boost::container::flat_map<std::string, sdbusplus::bus::match::match>
1844 dbusMatches;
James Feist75fdeeb2018-02-20 14:26:16 -08001845
James Feist4dc617b2020-05-01 09:54:47 -07001846 auto find = dbusMatches.find(interface);
1847 if (find != dbusMatches.end())
James Feist75fdeeb2018-02-20 14:26:16 -08001848 {
James Feist4dc617b2020-05-01 09:54:47 -07001849 return;
James Feist75fdeeb2018-02-20 14:26:16 -08001850 }
James Feist4dc617b2020-05-01 09:54:47 -07001851 std::function<void(sdbusplus::message::message & message)> eventHandler =
1852
1853 [&](sdbusplus::message::message&) {
1854 propertiesChangedCallback(systemConfiguration, objServer);
1855 };
1856
1857 sdbusplus::bus::match::match match(
1858 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
1859 "type='signal',member='PropertiesChanged',arg0='" + interface + "'",
1860 eventHandler);
1861 dbusMatches.emplace(interface, std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001862}
1863
James Feist98132792019-07-09 13:29:09 -07001864int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001865{
1866 // setup connection to dbus
James Feist8f2710a2018-05-09 17:18:55 -07001867 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001868 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001869
James Feist8f2710a2018-05-09 17:18:55 -07001870 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001871
James Feist8f2710a2018-05-09 17:18:55 -07001872 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1873 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1874 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001875
James Feist4131aea2018-03-09 09:47:30 -08001876 // to keep reference to the match / filter objects so they don't get
1877 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001878
1879 nlohmann::json systemConfiguration = nlohmann::json::object();
1880
James Feist4dc617b2020-05-01 09:54:47 -07001881 io.post(
1882 [&]() { propertiesChangedCallback(systemConfiguration, objServer); });
James Feist4131aea2018-03-09 09:47:30 -08001883
James Feistfd1264a2018-05-03 12:10:00 -07001884 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -07001885 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001886 });
James Feist8f2710a2018-05-09 17:18:55 -07001887 entityIface->initialize();
1888
James Feist1df06a42019-04-11 14:23:04 -07001889 if (fwVersionIsSame())
1890 {
1891 if (std::filesystem::is_regular_file(currentConfiguration))
1892 {
1893 // this file could just be deleted, but it's nice for debug
1894 std::filesystem::create_directory(tempConfigDir);
1895 std::filesystem::remove(lastConfiguration);
1896 std::filesystem::copy(currentConfiguration, lastConfiguration);
1897 std::filesystem::remove(currentConfiguration);
1898
1899 std::ifstream jsonStream(lastConfiguration);
1900 if (jsonStream.good())
1901 {
1902 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1903 if (data.is_discarded())
1904 {
1905 std::cerr << "syntax error in " << lastConfiguration
1906 << "\n";
1907 }
1908 else
1909 {
1910 lastJson = std::move(data);
1911 }
1912 }
1913 else
1914 {
1915 std::cerr << "unable to open " << lastConfiguration << "\n";
1916 }
1917 }
1918 }
1919 else
1920 {
1921 // not an error, just logging at this level to make it in the journal
1922 std::cerr << "Clearing previous configuration\n";
1923 std::filesystem::remove(currentConfiguration);
1924 }
1925
1926 // some boards only show up after power is on, we want to not say they are
1927 // removed until the same state happens
1928 setupPowerMatch(SYSTEM_BUS);
1929
James Feist1b2e2242018-01-30 13:45:19 -08001930 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001931
1932 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001933}