blob: 5b5e5ce53f52531fc2e553a3e97a71d832d6d2bf [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*/
Brad Bishop1fb9f3f2020-08-28 08:15:13 -040016/// \file EntityManager.cpp
James Feist3cb5fec2018-01-23 14:41:51 -080017
James Feist1df06a42019-04-11 14:23:04 -070018#include "EntityManager.hpp"
19
Patrick Venturea49dc332019-10-26 08:32:02 -070020#include "Overlay.hpp"
21#include "Utils.hpp"
James Feist481c5d52019-08-13 14:40:40 -070022#include "VariantVisitors.hpp"
23
James Feist11be6672018-04-06 14:05:32 -070024#include <boost/algorithm/string/case_conv.hpp>
James Feistf5125b02019-06-06 11:27:43 -070025#include <boost/algorithm/string/classification.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080026#include <boost/algorithm/string/predicate.hpp>
27#include <boost/algorithm/string/replace.hpp>
James Feistf5125b02019-06-06 11:27:43 -070028#include <boost/algorithm/string/split.hpp>
James Feist02d2b932020-02-06 16:28:48 -080029#include <boost/asio/io_context.hpp>
James Feistb1728ca2020-04-30 15:40:55 -070030#include <boost/asio/steady_timer.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080031#include <boost/container/flat_map.hpp>
32#include <boost/container/flat_set.hpp>
James Feistf5125b02019-06-06 11:27:43 -070033#include <boost/range/iterator_range.hpp>
James Feist8c505da2020-05-28 10:06:33 -070034#include <nlohmann/json.hpp>
35#include <sdbusplus/asio/connection.hpp>
36#include <sdbusplus/asio/object_server.hpp>
37
James Feist637b3ef2019-04-15 16:35:30 -070038#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080039#include <fstream>
40#include <iostream>
James Feista465ccc2019-02-08 12:51:01 -080041#include <regex>
James Feista465ccc2019-02-08 12:51:01 -080042#include <variant>
James Feista465ccc2019-02-08 12:51:01 -080043constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
44constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070045constexpr const char* tempConfigDir = "/tmp/configuration/";
46constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
47constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080048constexpr const char* globalSchema = "global.json";
James Feist8f2710a2018-05-09 17:18:55 -070049constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist3cb5fec2018-01-23 14:41:51 -080050
James Feistf1b14142019-04-10 15:22:09 -070051constexpr const bool DEBUG = false;
52
James Feist3cb5fec2018-01-23 14:41:51 -080053struct cmp_str
54{
James Feista465ccc2019-02-08 12:51:01 -080055 bool operator()(const char* a, const char* b) const
James Feist3cb5fec2018-01-23 14:41:51 -080056 {
57 return std::strcmp(a, b) < 0;
58 }
59};
60
61// underscore T for collison with dbus c api
62enum class probe_type_codes
63{
64 FALSE_T,
65 TRUE_T,
66 AND,
67 OR,
James Feist6bd2a022018-03-13 12:30:58 -070068 FOUND,
69 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080070};
James Feista465ccc2019-02-08 12:51:01 -080071const static boost::container::flat_map<const char*, probe_type_codes, cmp_str>
James Feist3cb5fec2018-01-23 14:41:51 -080072 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
73 {"TRUE", probe_type_codes::TRUE_T},
74 {"AND", probe_type_codes::AND},
75 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070076 {"FOUND", probe_type_codes::FOUND},
77 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080078
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +020079static constexpr std::array<const char*, 6> settableInterfaces = {
80 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
James Feist68500ff2018-08-08 15:40:42 -070081using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080082 std::variant<std::vector<std::string>, std::vector<double>, std::string,
83 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
84 uint16_t, uint8_t, bool>;
James Feist3cb5fec2018-01-23 14:41:51 -080085using GetSubTreeType = std::vector<
86 std::pair<std::string,
87 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
88
89using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070090 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080091 boost::container::flat_map<
92 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070093 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080094
James Feistd58879a2019-09-11 11:26:07 -070095// store reference to all interfaces so we can destroy them later
96boost::container::flat_map<
James Feist02d2b932020-02-06 16:28:48 -080097 std::string, std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>>
James Feistd58879a2019-09-11 11:26:07 -070098 inventory;
99
James Feist3cb5fec2018-01-23 14:41:51 -0800100// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -0700101std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist1df06a42019-04-11 14:23:04 -0700102static nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -0800103
James Feist02d2b932020-02-06 16:28:48 -0800104boost::asio::io_context io;
105
Johnathan Mantey2015f752019-03-26 15:22:31 -0700106const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
107const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800108
James Feist4dc617b2020-05-01 09:54:47 -0700109void registerCallback(nlohmann::json& systemConfiguration,
110 sdbusplus::asio::object_server& objServer,
111 const std::string& interfaces);
James Feist75fdeeb2018-02-20 14:26:16 -0800112
James Feistd58879a2019-09-11 11:26:07 -0700113static std::shared_ptr<sdbusplus::asio::dbus_interface>
114 createInterface(sdbusplus::asio::object_server& objServer,
115 const std::string& path, const std::string& interface,
James Feist02d2b932020-02-06 16:28:48 -0800116 const std::string& parent, bool checkNull = false)
James Feistd58879a2019-09-11 11:26:07 -0700117{
James Feist02d2b932020-02-06 16:28:48 -0800118 // on first add we have no reason to check for null before add, as there
119 // won't be any. For dynamically added interfaces, we check for null so that
120 // a constant delete/add will not create a memory leak
121
122 auto ptr = objServer.add_interface(path, interface);
123 auto& dataVector = inventory[parent];
124 if (checkNull)
125 {
126 auto it = std::find_if(dataVector.begin(), dataVector.end(),
127 [](const auto& p) { return p.expired(); });
128 if (it != dataVector.end())
129 {
130 *it = ptr;
131 return ptr;
132 }
133 }
134 dataVector.emplace_back(ptr);
135 return ptr;
James Feistd58879a2019-09-11 11:26:07 -0700136}
137
James Feistb1728ca2020-04-30 15:40:55 -0700138void getInterfaces(
139 const std::tuple<std::string, std::string, std::string>& call,
140 const std::vector<std::shared_ptr<PerformProbe>>& probeVector,
141 std::shared_ptr<PerformScan> scan, size_t retries = 5)
142{
143 if (!retries)
144 {
145 std::cerr << "retries exhausted on " << std::get<0>(call) << " "
146 << std::get<1>(call) << " " << std::get<2>(call) << "\n";
147 return;
148 }
149
150 SYSTEM_BUS->async_method_call(
151 [call, scan, probeVector, retries](
152 boost::system::error_code& errc,
153 const boost::container::flat_map<std::string, BasicVariantType>&
154 resp) {
155 if (errc)
156 {
157 std::cerr << "error calling getall on " << std::get<0>(call)
158 << " " << std::get<1>(call) << " "
159 << std::get<2>(call) << "\n";
160
161 std::shared_ptr<boost::asio::steady_timer> timer =
162 std::make_shared<boost::asio::steady_timer>(io);
163 timer->expires_after(std::chrono::seconds(2));
164
165 timer->async_wait([timer, call, scan, probeVector,
166 retries](const boost::system::error_code&) {
167 getInterfaces(call, probeVector, scan, retries - 1);
168 });
169 return;
170 }
171
172 scan->dbusProbeObjects[std::get<2>(call)].emplace_back(resp);
173 },
174 std::get<0>(call), std::get<1>(call), "org.freedesktop.DBus.Properties",
175 "GetAll", std::get<2>(call));
176
177 if constexpr (DEBUG)
178 {
179 std::cerr << __func__ << " " << __LINE__ << "\n";
180 }
181}
182
James Feist3cb5fec2018-01-23 14:41:51 -0800183// calls the mapper to find all exposed objects of an interface type
184// and creates a vector<flat_map> that contains all the key value pairs
185// getManagedObjects
James Feistb1728ca2020-04-30 15:40:55 -0700186void findDbusObjects(std::vector<std::shared_ptr<PerformProbe>>&& probeVector,
187 boost::container::flat_set<std::string>&& interfaces,
James Feist32d1f0a2020-09-10 14:49:25 -0700188 std::shared_ptr<PerformScan> scan, size_t retries = 5)
James Feist3cb5fec2018-01-23 14:41:51 -0800189{
James Feist8f2710a2018-05-09 17:18:55 -0700190
James Feist733f7652019-11-13 14:32:29 -0800191 for (const auto& [interface, _] : scan->dbusProbeObjects)
192 {
193 interfaces.erase(interface);
194 }
195 if (interfaces.empty())
196 {
197 return;
198 }
199
James Feist3cb5fec2018-01-23 14:41:51 -0800200 // find all connections in the mapper that expose a specific type
James Feistb1728ca2020-04-30 15:40:55 -0700201 SYSTEM_BUS->async_method_call(
202 [interfaces{std::move(interfaces)}, probeVector{std::move(probeVector)},
James Feist32d1f0a2020-09-10 14:49:25 -0700203 scan, retries](boost::system::error_code& ec,
204 const GetSubTreeType& interfaceSubtree) mutable {
James Feistb1728ca2020-04-30 15:40:55 -0700205 boost::container::flat_set<
206 std::tuple<std::string, std::string, std::string>>
207 interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700208 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700209 {
James Feist0de40152018-07-25 11:56:12 -0700210 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700211 {
James Feist0de40152018-07-25 11:56:12 -0700212 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700213 }
James Feist0de40152018-07-25 11:56:12 -0700214 std::cerr << "Error communicating to mapper.\n";
215
James Feist32d1f0a2020-09-10 14:49:25 -0700216 if (!retries)
217 {
218 // if we can't communicate to the mapper something is very
219 // wrong
220 std::exit(EXIT_FAILURE);
221 }
222 std::shared_ptr<boost::asio::steady_timer> timer =
223 std::make_shared<boost::asio::steady_timer>(io);
224 timer->expires_after(std::chrono::seconds(10));
225
226 timer->async_wait(
227 [timer, interfaces{std::move(interfaces)}, scan,
228 probeVector{std::move(probeVector)},
229 retries](const boost::system::error_code&) mutable {
230 findDbusObjects(std::move(probeVector),
231 std::move(interfaces), scan,
232 retries - 1);
233 });
234 return;
James Feist494155a2018-03-14 16:23:24 -0700235 }
James Feist787c3c32019-11-07 14:42:58 -0800236
James Feistb1728ca2020-04-30 15:40:55 -0700237 for (const auto& [path, object] : interfaceSubtree)
James Feist3cb5fec2018-01-23 14:41:51 -0800238 {
James Feistb1728ca2020-04-30 15:40:55 -0700239 for (const auto& [busname, ifaces] : object)
James Feist8f2710a2018-05-09 17:18:55 -0700240 {
James Feistb1728ca2020-04-30 15:40:55 -0700241 for (const std::string& iface : ifaces)
242 {
243 auto ifaceObjFind = interfaces.find(iface);
244
245 if (ifaceObjFind != interfaces.end())
246 {
247 interfaceConnections.emplace(busname, path, iface);
248 }
249 }
James Feist8f2710a2018-05-09 17:18:55 -0700250 }
James Feist3cb5fec2018-01-23 14:41:51 -0800251 }
James Feist787c3c32019-11-07 14:42:58 -0800252
James Feist63845bf2019-01-24 12:19:51 -0800253 if (interfaceConnections.empty())
254 {
James Feist63845bf2019-01-24 12:19:51 -0800255 return;
256 }
James Feist787c3c32019-11-07 14:42:58 -0800257
James Feistb1728ca2020-04-30 15:40:55 -0700258 for (const auto& call : interfaceConnections)
259 {
260 getInterfaces(call, probeVector, scan);
James Feist8f2710a2018-05-09 17:18:55 -0700261 }
262 },
263 "xyz.openbmc_project.ObjectMapper",
264 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700265 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist787c3c32019-11-07 14:42:58 -0800266 interfaces);
James Feistb1728ca2020-04-30 15:40:55 -0700267
268 if constexpr (DEBUG)
269 {
270 std::cerr << __func__ << " " << __LINE__ << "\n";
271 }
James Feist3cb5fec2018-01-23 14:41:51 -0800272}
James Feistb1728ca2020-04-30 15:40:55 -0700273
James Feist8f2710a2018-05-09 17:18:55 -0700274// probes dbus interface dictionary for a key with a value that matches a regex
James Feist08a5b172019-08-28 14:47:47 -0700275bool probeDbus(const std::string& interface,
276 const std::map<std::string, nlohmann::json>& matches,
James Feist733f7652019-11-13 14:32:29 -0800277 FoundDeviceT& devices, std::shared_ptr<PerformScan> scan,
278 bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800279{
James Feista465ccc2019-02-08 12:51:01 -0800280 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
James Feist733f7652019-11-13 14:32:29 -0800281 dbusObject = scan->dbusProbeObjects[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800282 if (dbusObject.empty())
283 {
James Feist8f2710a2018-05-09 17:18:55 -0700284 foundProbe = false;
285 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800286 }
287 foundProbe = true;
288
289 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800290 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800291 {
292 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800293 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800294 {
295 auto deviceValue = device.find(match.first);
296 if (deviceValue != device.end())
297 {
Brad Bishop3cb8a602020-08-25 17:40:54 -0400298 deviceMatches = matchProbe(match.second, deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800299 }
300 else
301 {
302 deviceMatches = false;
303 break;
304 }
305 }
306 if (deviceMatches)
307 {
James Feistf5125b02019-06-06 11:27:43 -0700308 devices.emplace_back(device);
James Feist3cb5fec2018-01-23 14:41:51 -0800309 foundMatch = true;
310 deviceMatches = false; // for next iteration
311 }
312 }
313 return foundMatch;
314}
315
316// default probe entry point, iterates a list looking for specific types to
317// call specific probe functions
318bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800319 const std::vector<std::string>& probeCommand,
James Feist733f7652019-11-13 14:32:29 -0800320 std::shared_ptr<PerformScan> scan,
James Feist08a5b172019-08-28 14:47:47 -0700321 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
322 foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800323{
324 const static std::regex command(R"(\((.*)\))");
325 std::smatch match;
326 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700327 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800328 bool cur = true;
329 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
James Feist54a0dca2019-06-26 10:34:54 -0700330 bool first = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800331
James Feista465ccc2019-02-08 12:51:01 -0800332 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800333 {
334 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800335 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800336 cmp_str>::const_iterator probeType;
337
338 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
Patrick Venture4b7a7452019-10-28 08:41:02 -0700339 ++probeType)
James Feist3cb5fec2018-01-23 14:41:51 -0800340 {
341 if (probe.find(probeType->first) != std::string::npos)
342 {
343 foundProbe = true;
344 break;
345 }
346 }
347 if (foundProbe)
348 {
349 switch (probeType->second)
350 {
James Feist9eb0b582018-04-27 12:15:46 -0700351 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800352 {
James Feiste31e00a2019-07-24 10:45:43 -0700353 cur = false;
354 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800355 }
James Feist9eb0b582018-04-27 12:15:46 -0700356 case probe_type_codes::TRUE_T:
357 {
James Feiste31e00a2019-07-24 10:45:43 -0700358 cur = true;
359 break;
James Feist9eb0b582018-04-27 12:15:46 -0700360 }
361 case probe_type_codes::MATCH_ONE:
362 {
363 // set current value to last, this probe type shouldn't
364 // affect the outcome
365 cur = ret;
366 matchOne = true;
367 break;
368 }
369 /*case probe_type_codes::AND:
370 break;
371 case probe_type_codes::OR:
372 break;
373 // these are no-ops until the last command switch
374 */
375 case probe_type_codes::FOUND:
376 {
377 if (!std::regex_search(probe, match, command))
378 {
James Feist0eb40352019-04-09 14:44:04 -0700379 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700380 << "\n";
381 return false;
382 }
383 std::string commandStr = *(match.begin() + 1);
384 boost::replace_all(commandStr, "'", "");
James Feist733f7652019-11-13 14:32:29 -0800385 cur = (std::find(scan->passedProbes.begin(),
386 scan->passedProbes.end(),
387 commandStr) != scan->passedProbes.end());
James Feist9eb0b582018-04-27 12:15:46 -0700388 break;
389 }
James Feist0eb40352019-04-09 14:44:04 -0700390 default:
391 {
392 break;
393 }
James Feist3cb5fec2018-01-23 14:41:51 -0800394 }
395 }
396 // look on dbus for object
397 else
398 {
399 if (!std::regex_search(probe, match, command))
400 {
James Feist0eb40352019-04-09 14:44:04 -0700401 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800402 return false;
403 }
404 std::string commandStr = *(match.begin() + 1);
405 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700406 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800407 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800408 auto json = nlohmann::json::parse(commandStr, nullptr, false);
409 if (json.is_discarded())
410 {
James Feist0eb40352019-04-09 14:44:04 -0700411 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800412 return false;
413 }
414 // we can match any (string, variant) property. (string, string)
415 // does a regex
416 std::map<std::string, nlohmann::json> dbusProbeMap =
417 json.get<std::map<std::string, nlohmann::json>>();
418 auto findStart = probe.find("(");
419 if (findStart == std::string::npos)
420 {
421 return false;
422 }
423 std::string probeInterface = probe.substr(0, findStart);
James Feist733f7652019-11-13 14:32:29 -0800424 cur = probeDbus(probeInterface, dbusProbeMap, foundDevs, scan,
425 foundProbe);
James Feist3cb5fec2018-01-23 14:41:51 -0800426 }
427
428 // some functions like AND and OR only take affect after the
429 // fact
James Feist54a0dca2019-06-26 10:34:54 -0700430 if (lastCommand == probe_type_codes::AND)
James Feist3cb5fec2018-01-23 14:41:51 -0800431 {
James Feist54a0dca2019-06-26 10:34:54 -0700432 ret = cur && ret;
433 }
434 else if (lastCommand == probe_type_codes::OR)
435 {
436 ret = cur || ret;
437 }
438
439 if (first)
440 {
441 ret = cur;
442 first = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800443 }
444 lastCommand = probeType != PROBE_TYPES.end()
445 ? probeType->second
446 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800447 }
448
449 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800450 if (ret && foundDevs.size() == 0)
451 {
James Feist08a5b172019-08-28 14:47:47 -0700452 foundDevs.emplace_back(
453 boost::container::flat_map<std::string, BasicVariantType>{});
James Feist3cb5fec2018-01-23 14:41:51 -0800454 }
James Feist0eb40352019-04-09 14:44:04 -0700455 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700456 {
James Feist71f295f2019-06-20 13:35:12 -0700457 // match the last one
458 auto last = foundDevs.back();
James Feist0eb40352019-04-09 14:44:04 -0700459 foundDevs.clear();
James Feist71f295f2019-06-20 13:35:12 -0700460
461 foundDevs.emplace_back(std::move(last));
James Feist6bd2a022018-03-13 12:30:58 -0700462 }
James Feist3cb5fec2018-01-23 14:41:51 -0800463 return ret;
464}
465
James Feist7d807752019-11-13 14:47:57 -0800466PerformProbe::PerformProbe(const std::vector<std::string>& probeCommand,
467 std::shared_ptr<PerformScan>& scanPtr,
468 std::function<void(FoundDeviceT&)>&& callback) :
469 _probeCommand(probeCommand),
470 scan(scanPtr), _callback(std::move(callback))
James Feist8c505da2020-05-28 10:06:33 -0700471{}
James Feist7d807752019-11-13 14:47:57 -0800472PerformProbe::~PerformProbe()
473{
474 FoundDeviceT foundDevs;
475 if (probe(_probeCommand, scan, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700476 {
James Feist7d807752019-11-13 14:47:57 -0800477 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700478 }
James Feist7d807752019-11-13 14:47:57 -0800479}
James Feist8f2710a2018-05-09 17:18:55 -0700480
481// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800482bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800483{
James Feist1df06a42019-04-11 14:23:04 -0700484 std::filesystem::create_directory(configurationOutDir);
485 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700486 if (!output.good())
487 {
488 return false;
489 }
James Feist1b2e2242018-01-30 13:45:19 -0800490 output << systemConfiguration.dump(4);
491 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700492 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700493}
James Feist1b2e2242018-01-30 13:45:19 -0800494
James Feist97a63f12018-05-17 13:50:57 -0700495template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800496bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
497 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700498{
499 try
500 {
501 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800502 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700503 ref = value;
504 return true;
505 }
James Feist98132792019-07-09 13:29:09 -0700506 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700507 {
508 return false;
509 }
510}
James Feistbb43d022018-06-12 15:44:33 -0700511
James Feistebcc26b2019-03-22 12:30:43 -0700512// template function to add array as dbus property
513template <typename PropertyType>
514void addArrayToDbus(const std::string& name, const nlohmann::json& array,
515 sdbusplus::asio::dbus_interface* iface,
516 sdbusplus::asio::PropertyPermission permission,
517 nlohmann::json& systemConfiguration,
518 const std::string& jsonPointerString)
519{
520 std::vector<PropertyType> values;
521 for (const auto& property : array)
522 {
523 auto ptr = property.get_ptr<const PropertyType*>();
524 if (ptr != nullptr)
525 {
526 values.emplace_back(*ptr);
527 }
528 }
529
530 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
531 {
532 iface->register_property(name, values);
533 }
534 else
535 {
536 iface->register_property(
537 name, values,
538 [&systemConfiguration,
539 jsonPointerString{std::string(jsonPointerString)}](
540 const std::vector<PropertyType>& newVal,
541 std::vector<PropertyType>& val) {
542 val = newVal;
543 if (!setJsonFromPointer(jsonPointerString, val,
544 systemConfiguration))
545 {
546 std::cerr << "error setting json field\n";
547 return -1;
548 }
549 if (!writeJsonFiles(systemConfiguration))
550 {
551 std::cerr << "error setting json file\n";
552 return -1;
553 }
554 return 1;
555 });
556 }
557}
558
James Feistbb43d022018-06-12 15:44:33 -0700559template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800560void addProperty(const std::string& propertyName, const PropertyType& value,
561 sdbusplus::asio::dbus_interface* iface,
562 nlohmann::json& systemConfiguration,
563 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700564 sdbusplus::asio::PropertyPermission permission)
565{
566 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
567 {
568 iface->register_property(propertyName, value);
569 return;
570 }
James Feist68500ff2018-08-08 15:40:42 -0700571 iface->register_property(
572 propertyName, value,
573 [&systemConfiguration,
574 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800575 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700576 val = newVal;
577 if (!setJsonFromPointer(jsonPointerString, val,
578 systemConfiguration))
579 {
580 std::cerr << "error setting json field\n";
581 return -1;
582 }
James Feistc6248a52018-08-14 10:09:45 -0700583 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700584 {
585 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700586 return -1;
587 }
588 return 1;
589 });
590}
591
592void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800593 const std::string& jsonPointerPath,
594 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
595 sdbusplus::asio::object_server& objServer,
596 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700597{
598 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
599 iface->register_method(
600 "Delete", [&objServer, &systemConfiguration, interface,
601 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700602 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700603 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700604 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700605 {
606 // this technically can't happen as the pointer is pointing to
607 // us
608 throw DBusInternalError();
609 }
610 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feistc6248a52018-08-14 10:09:45 -0700611 systemConfiguration[ptr] = nullptr;
612
James Feist02d2b932020-02-06 16:28:48 -0800613 // todo(james): dig through sdbusplus to find out why we can't
614 // delete it in a method call
615 io.post([&objServer, dbusInterface]() mutable {
616 objServer.remove_interface(dbusInterface);
617 });
618
James Feistc6248a52018-08-14 10:09:45 -0700619 if (!writeJsonFiles(systemConfiguration))
620 {
621 std::cerr << "error setting json file\n";
622 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700623 }
James Feist68500ff2018-08-08 15:40:42 -0700624 });
James Feistbb43d022018-06-12 15:44:33 -0700625}
626
James Feist1b2e2242018-01-30 13:45:19 -0800627// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700628void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800629 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
630 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
631 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700632 sdbusplus::asio::PropertyPermission permission =
633 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800634{
James Feista465ccc2019-02-08 12:51:01 -0800635 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800636 {
James Feist8f2710a2018-05-09 17:18:55 -0700637 auto type = dictPair.value().type();
638 bool array = false;
639 if (dictPair.value().type() == nlohmann::json::value_t::array)
640 {
641 array = true;
642 if (!dictPair.value().size())
643 {
644 continue;
645 }
646 type = dictPair.value()[0].type();
647 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800648 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700649 {
650 if (arrayItem.type() != type)
651 {
652 isLegal = false;
653 break;
654 }
655 }
656 if (!isLegal)
657 {
658 std::cerr << "dbus format error" << dictPair.value() << "\n";
659 continue;
660 }
James Feista218ddb2019-04-11 14:01:31 -0700661 }
662 if (type == nlohmann::json::value_t::object)
663 {
664 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700665 }
James Feist97a63f12018-05-17 13:50:57 -0700666 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700667 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
668 {
669 // all setable numbers are doubles as it is difficult to always
670 // create a configuration file with all whole numbers as decimals
671 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700672 if (array)
673 {
674 if (dictPair.value()[0].is_number())
675 {
676 type = nlohmann::json::value_t::number_float;
677 }
678 }
679 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700680 {
681 type = nlohmann::json::value_t::number_float;
682 }
683 }
684
James Feist8f2710a2018-05-09 17:18:55 -0700685 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800686 {
James Feist9eb0b582018-04-27 12:15:46 -0700687 case (nlohmann::json::value_t::boolean):
688 {
James Feist8f2710a2018-05-09 17:18:55 -0700689 if (array)
690 {
691 // todo: array of bool isn't detected correctly by
692 // sdbusplus, change it to numbers
693 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700694 iface.get(), permission,
695 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700696 }
James Feistbb43d022018-06-12 15:44:33 -0700697
James Feist97a63f12018-05-17 13:50:57 -0700698 else
699 {
James Feistbb43d022018-06-12 15:44:33 -0700700 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700701 iface.get(), systemConfiguration, key,
702 permission);
James Feist97a63f12018-05-17 13:50:57 -0700703 }
James Feist9eb0b582018-04-27 12:15:46 -0700704 break;
705 }
706 case (nlohmann::json::value_t::number_integer):
707 {
James Feist8f2710a2018-05-09 17:18:55 -0700708 if (array)
709 {
710 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700711 iface.get(), permission,
712 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700713 }
714 else
715 {
James Feistbb43d022018-06-12 15:44:33 -0700716 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700717 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700718 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700719 }
James Feist9eb0b582018-04-27 12:15:46 -0700720 break;
721 }
722 case (nlohmann::json::value_t::number_unsigned):
723 {
James Feist8f2710a2018-05-09 17:18:55 -0700724 if (array)
725 {
726 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700727 iface.get(), permission,
728 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700729 }
730 else
731 {
James Feistbb43d022018-06-12 15:44:33 -0700732 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700733 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700734 systemConfiguration, key,
735 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700736 }
James Feist9eb0b582018-04-27 12:15:46 -0700737 break;
738 }
739 case (nlohmann::json::value_t::number_float):
740 {
James Feist8f2710a2018-05-09 17:18:55 -0700741 if (array)
742 {
743 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700744 iface.get(), permission,
745 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700746 }
James Feistbb43d022018-06-12 15:44:33 -0700747
James Feist97a63f12018-05-17 13:50:57 -0700748 else
749 {
James Feistbb43d022018-06-12 15:44:33 -0700750 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700751 iface.get(), systemConfiguration, key,
752 permission);
James Feist97a63f12018-05-17 13:50:57 -0700753 }
James Feist9eb0b582018-04-27 12:15:46 -0700754 break;
755 }
756 case (nlohmann::json::value_t::string):
757 {
James Feist8f2710a2018-05-09 17:18:55 -0700758 if (array)
759 {
James Feistebcc26b2019-03-22 12:30:43 -0700760 addArrayToDbus<std::string>(
761 dictPair.key(), dictPair.value(), iface.get(),
762 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700763 }
764 else
765 {
James Feistc6248a52018-08-14 10:09:45 -0700766 addProperty(
767 dictPair.key(), dictPair.value().get<std::string>(),
768 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700769 }
James Feist9eb0b582018-04-27 12:15:46 -0700770 break;
771 }
James Feist0eb40352019-04-09 14:44:04 -0700772 default:
773 {
James Feista218ddb2019-04-11 14:01:31 -0700774 std::cerr << "Unexpected json type in system configuration "
775 << dictPair.key() << ": "
776 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700777 break;
778 }
James Feist1b2e2242018-01-30 13:45:19 -0800779 }
780 }
James Feistc6248a52018-08-14 10:09:45 -0700781 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
782 {
783 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
784 systemConfiguration);
785 }
James Feist8f2710a2018-05-09 17:18:55 -0700786 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800787}
788
James Feista465ccc2019-02-08 12:51:01 -0800789sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700790{
791 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
792 interface) != settableInterfaces.end()
793 ? sdbusplus::asio::PropertyPermission::readWrite
794 : sdbusplus::asio::PropertyPermission::readOnly;
795}
796
James Feista465ccc2019-02-08 12:51:01 -0800797void createAddObjectMethod(const std::string& jsonPointerPath,
798 const std::string& path,
799 nlohmann::json& systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700800 sdbusplus::asio::object_server& objServer,
801 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700802{
James Feistd58879a2019-09-11 11:26:07 -0700803 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
804 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700805
806 iface->register_method(
807 "AddObject",
808 [&systemConfiguration, &objServer,
James Feistd58879a2019-09-11 11:26:07 -0700809 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
810 board](const boost::container::flat_map<std::string, JsonVariantType>&
811 data) {
James Feist68500ff2018-08-08 15:40:42 -0700812 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800813 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700814 auto findExposes = base.find("Exposes");
815
816 if (findExposes == base.end())
817 {
818 throw std::invalid_argument("Entity must have children.");
819 }
820
821 // this will throw invalid-argument to sdbusplus if invalid json
822 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800823 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700824 {
James Feista465ccc2019-02-08 12:51:01 -0800825 nlohmann::json& newJson = newData[item.first];
826 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
827 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700828 }
829
830 auto findName = newData.find("Name");
831 auto findType = newData.find("Type");
832 if (findName == newData.end() || findType == newData.end())
833 {
834 throw std::invalid_argument("AddObject missing Name or Type");
835 }
James Feista465ccc2019-02-08 12:51:01 -0800836 const std::string* type = findType->get_ptr<const std::string*>();
837 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700838 if (type == nullptr || name == nullptr)
839 {
840 throw std::invalid_argument("Type and Name must be a string.");
841 }
842
James Feist02d2b932020-02-06 16:28:48 -0800843 bool foundNull = false;
James Feist68500ff2018-08-08 15:40:42 -0700844 size_t lastIndex = 0;
845 // we add in the "exposes"
James Feist02d2b932020-02-06 16:28:48 -0800846 for (const auto& expose : *findExposes)
James Feist68500ff2018-08-08 15:40:42 -0700847 {
James Feist02d2b932020-02-06 16:28:48 -0800848 if (expose.is_null())
849 {
850 foundNull = true;
851 continue;
852 }
853
854 if (expose["Name"] == *name && expose["Type"] == *type)
James Feist68500ff2018-08-08 15:40:42 -0700855 {
856 throw std::invalid_argument(
857 "Field already in JSON, not adding");
858 }
James Feist02d2b932020-02-06 16:28:48 -0800859
860 if (foundNull)
861 {
862 continue;
863 }
864
James Feist68500ff2018-08-08 15:40:42 -0700865 lastIndex++;
866 }
867
868 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
869 *type + ".json");
870 // todo(james) we might want to also make a list of 'can add'
871 // interfaces but for now I think the assumption if there is a
872 // schema avaliable that it is allowed to update is fine
873 if (!schemaFile.good())
874 {
875 throw std::invalid_argument(
876 "No schema avaliable, cannot validate.");
877 }
878 nlohmann::json schema =
879 nlohmann::json::parse(schemaFile, nullptr, false);
880 if (schema.is_discarded())
881 {
882 std::cerr << "Schema not legal" << *type << ".json\n";
883 throw DBusInternalError();
884 }
885 if (!validateJson(schema, newData))
886 {
887 throw std::invalid_argument("Data does not match schema");
888 }
James Feist02d2b932020-02-06 16:28:48 -0800889 if (foundNull)
890 {
891 findExposes->at(lastIndex) = newData;
892 }
893 else
894 {
895 findExposes->push_back(newData);
896 }
James Feist68500ff2018-08-08 15:40:42 -0700897 if (!writeJsonFiles(systemConfiguration))
898 {
899 std::cerr << "Error writing json files\n";
900 throw DBusInternalError();
901 }
902 std::string dbusName = *name;
903
904 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700905 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistd58879a2019-09-11 11:26:07 -0700906
907 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
908 createInterface(objServer, path + "/" + dbusName,
909 "xyz.openbmc_project.Configuration." + *type,
James Feist02d2b932020-02-06 16:28:48 -0800910 board, true);
James Feist68500ff2018-08-08 15:40:42 -0700911 // permission is read-write, as since we just created it, must be
912 // runtime modifiable
913 populateInterfaceFromJson(
914 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700915 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700916 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700917 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700918 });
919 iface->initialize();
920}
921
James Feista465ccc2019-02-08 12:51:01 -0800922void postToDbus(const nlohmann::json& newConfiguration,
923 nlohmann::json& systemConfiguration,
924 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800925
James Feist1b2e2242018-01-30 13:45:19 -0800926{
James Feist97a63f12018-05-17 13:50:57 -0700927 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800928 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800929 {
James Feistf1b14142019-04-10 15:22:09 -0700930 std::string boardKey = boardPair.value()["Name"];
James Feistd58879a2019-09-11 11:26:07 -0700931 std::string boardKeyOrig = boardPair.value()["Name"];
James Feistf1b14142019-04-10 15:22:09 -0700932 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700933 // loop through newConfiguration, but use values from system
934 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700935 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700936 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800937 std::string boardType;
938 if (findBoardType != boardValues.end() &&
939 findBoardType->type() == nlohmann::json::value_t::string)
940 {
941 boardType = findBoardType->get<std::string>();
942 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700943 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800944 }
945 else
946 {
947 std::cerr << "Unable to find type for " << boardKey
948 << " reverting to Chassis.\n";
949 boardType = "Chassis";
950 }
James Feist11be6672018-04-06 14:05:32 -0700951 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800952
953 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700954 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700955 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
956 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800957
James Feistd58879a2019-09-11 11:26:07 -0700958 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
959 createInterface(objServer, boardName,
960 "xyz.openbmc_project.Inventory.Item", boardKey);
James Feist68500ff2018-08-08 15:40:42 -0700961
James Feistd58879a2019-09-11 11:26:07 -0700962 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
963 createInterface(objServer, boardName,
964 "xyz.openbmc_project.Inventory.Item." + boardType,
965 boardKeyOrig);
James Feist11be6672018-04-06 14:05:32 -0700966
James Feist68500ff2018-08-08 15:40:42 -0700967 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700968 objServer, boardKeyOrig);
James Feist68500ff2018-08-08 15:40:42 -0700969
James Feist97a63f12018-05-17 13:50:57 -0700970 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700971 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700972 jsonPointerPath += "/";
973 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800974 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700975 {
976 if (boardField.value().type() == nlohmann::json::value_t::object)
977 {
James Feistd58879a2019-09-11 11:26:07 -0700978 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
979 createInterface(objServer, boardName, boardField.key(),
980 boardKeyOrig);
981
James Feistc6248a52018-08-14 10:09:45 -0700982 populateInterfaceFromJson(systemConfiguration,
983 jsonPointerPath + boardField.key(),
984 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700985 }
986 }
James Feist97a63f12018-05-17 13:50:57 -0700987
James Feist1e3e6982018-08-03 16:09:28 -0700988 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800989 if (exposes == boardValues.end())
990 {
991 continue;
992 }
James Feist97a63f12018-05-17 13:50:57 -0700993 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700994 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700995
996 // store the board level pointer so we can modify it on the way down
997 std::string jsonPointerPathBoard = jsonPointerPath;
998 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -0800999 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001000 {
James Feist97a63f12018-05-17 13:50:57 -07001001 exposesIndex++;
1002 jsonPointerPath = jsonPointerPathBoard;
1003 jsonPointerPath += std::to_string(exposesIndex);
1004
James Feistd63d18a2018-07-19 15:23:45 -07001005 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001006 if (findName == item.end())
1007 {
1008 std::cerr << "cannot find name in field " << item << "\n";
1009 continue;
1010 }
James Feist1e3e6982018-08-03 16:09:28 -07001011 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001012 // if status is not found it is assumed to be status = 'okay'
1013 if (findStatus != item.end())
1014 {
1015 if (*findStatus == "disabled")
1016 {
1017 continue;
1018 }
1019 }
James Feistd63d18a2018-07-19 15:23:45 -07001020 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001021 std::string itemType;
1022 if (findType != item.end())
1023 {
1024 itemType = findType->get<std::string>();
1025 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001026 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1027 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001028 }
1029 else
1030 {
1031 itemType = "unknown";
1032 }
1033 std::string itemName = findName->get<std::string>();
1034 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001035 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001036
James Feistd58879a2019-09-11 11:26:07 -07001037 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
1038 createInterface(objServer, boardName + "/" + itemName,
1039 "xyz.openbmc_project.Configuration." + itemType,
1040 boardKeyOrig);
James Feist1b2e2242018-01-30 13:45:19 -08001041
James Feist97a63f12018-05-17 13:50:57 -07001042 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001043 itemIface, item, objServer,
1044 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001045
James Feista465ccc2019-02-08 12:51:01 -08001046 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001047 {
James Feist97a63f12018-05-17 13:50:57 -07001048 jsonPointerPath = jsonPointerPathBoard +
1049 std::to_string(exposesIndex) + "/" +
1050 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001051 if (objectPair.value().type() ==
1052 nlohmann::json::value_t::object)
1053 {
James Feistd58879a2019-09-11 11:26:07 -07001054 std::shared_ptr<sdbusplus::asio::dbus_interface>
1055 objectIface = createInterface(
1056 objServer, boardName + "/" + itemName,
1057 "xyz.openbmc_project.Configuration." + itemType +
1058 "." + objectPair.key(),
1059 boardKeyOrig);
James Feist97a63f12018-05-17 13:50:57 -07001060
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +02001061 populateInterfaceFromJson(systemConfiguration,
1062 jsonPointerPath, objectIface,
1063 objectPair.value(), objServer,
1064 getPermission(objectPair.key()));
James Feist1b2e2242018-01-30 13:45:19 -08001065 }
1066 else if (objectPair.value().type() ==
1067 nlohmann::json::value_t::array)
1068 {
1069 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001070 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001071 {
James Feist8f2710a2018-05-09 17:18:55 -07001072 continue;
1073 }
1074 bool isLegal = true;
1075 auto type = objectPair.value()[0].type();
1076 if (type != nlohmann::json::value_t::object)
1077 {
1078 continue;
1079 }
1080
1081 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001082 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001083 {
1084 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001085 {
James Feist8f2710a2018-05-09 17:18:55 -07001086 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001087 break;
1088 }
James Feist8f2710a2018-05-09 17:18:55 -07001089 }
1090 if (!isLegal)
1091 {
1092 std::cerr << "dbus format error" << objectPair.value()
1093 << "\n";
1094 break;
1095 }
1096
James Feista465ccc2019-02-08 12:51:01 -08001097 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001098 {
James Feist97a63f12018-05-17 13:50:57 -07001099
James Feistd58879a2019-09-11 11:26:07 -07001100 std::shared_ptr<sdbusplus::asio::dbus_interface>
1101 objectIface = createInterface(
1102 objServer, boardName + "/" + itemName,
1103 "xyz.openbmc_project.Configuration." +
1104 itemType + "." + objectPair.key() +
1105 std::to_string(index),
1106 boardKeyOrig);
1107
James Feistc6248a52018-08-14 10:09:45 -07001108 populateInterfaceFromJson(
1109 systemConfiguration,
1110 jsonPointerPath + "/" + std::to_string(index),
1111 objectIface, arrayItem, objServer,
1112 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001113 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001114 }
1115 }
1116 }
1117 }
1118 }
1119}
1120
James Feist8f2710a2018-05-09 17:18:55 -07001121// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001122bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001123{
1124 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001125 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001126 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1127 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001128 {
1129 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001130 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001131 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001132 }
James Feistb4383f42018-08-06 16:54:10 -07001133
1134 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1135 globalSchema);
1136 if (!schemaStream.good())
1137 {
1138 std::cerr
1139 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1140 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001141 return false;
James Feistb4383f42018-08-06 16:54:10 -07001142 }
1143 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1144 if (schema.is_discarded())
1145 {
1146 std::cerr
1147 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1148 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001149 return false;
James Feistb4383f42018-08-06 16:54:10 -07001150 }
1151
James Feista465ccc2019-02-08 12:51:01 -08001152 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001153 {
1154 std::ifstream jsonStream(jsonPath.c_str());
1155 if (!jsonStream.good())
1156 {
1157 std::cerr << "unable to open " << jsonPath.string() << "\n";
1158 continue;
1159 }
1160 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1161 if (data.is_discarded())
1162 {
1163 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1164 continue;
1165 }
James Feist8da99192019-01-24 08:20:16 -08001166 /*
1167 * todo(james): reenable this once less things are in flight
1168 *
James Feistb4383f42018-08-06 16:54:10 -07001169 if (!validateJson(schema, data))
1170 {
1171 std::cerr << "Error validating " << jsonPath.string() << "\n";
1172 continue;
1173 }
James Feist8da99192019-01-24 08:20:16 -08001174 */
James Feistb4383f42018-08-06 16:54:10 -07001175
James Feist3cb5fec2018-01-23 14:41:51 -08001176 if (data.type() == nlohmann::json::value_t::array)
1177 {
James Feista465ccc2019-02-08 12:51:01 -08001178 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001179 {
1180 configurations.emplace_back(d);
1181 }
1182 }
1183 else
1184 {
1185 configurations.emplace_back(data);
1186 }
1187 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001188 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001189}
James Feist3cb5fec2018-01-23 14:41:51 -08001190
James Feist94219d12020-03-03 11:52:25 -08001191std::string getRecordName(
1192 const boost::container::flat_map<std::string, BasicVariantType>& probe,
1193 const std::string& probeName)
1194{
1195 if (probe.empty())
1196 {
1197 return probeName;
1198 }
1199
1200 // use an array so alphabetical order from the
1201 // flat_map is maintained
1202 auto device = nlohmann::json::array();
1203 for (auto& devPair : probe)
1204 {
1205 device.push_back(devPair.first);
1206 std::visit([&device](auto&& v) { device.push_back(v); },
1207 devPair.second);
1208 }
1209 size_t hash = std::hash<std::string>{}(probeName + device.dump());
1210 // hashes are hard to distinguish, use the
1211 // non-hashed version if we want debug
1212 if constexpr (DEBUG)
1213 {
1214 return probeName + device.dump();
1215 }
1216 else
1217 {
1218 return std::to_string(hash);
1219 }
1220}
1221
James Feist4dc617b2020-05-01 09:54:47 -07001222PerformScan::PerformScan(nlohmann::json& systemConfiguration,
1223 nlohmann::json& missingConfigurations,
1224 std::list<nlohmann::json>& configurations,
1225 sdbusplus::asio::object_server& objServerIn,
1226 std::function<void()>&& callback) :
James Feist733f7652019-11-13 14:32:29 -08001227 _systemConfiguration(systemConfiguration),
1228 _missingConfigurations(missingConfigurations),
James Feist4dc617b2020-05-01 09:54:47 -07001229 _configurations(configurations), objServer(objServerIn),
1230 _callback(std::move(callback))
James Feist8c505da2020-05-28 10:06:33 -07001231{}
James Feist733f7652019-11-13 14:32:29 -08001232void PerformScan::run()
1233{
1234 boost::container::flat_set<std::string> dbusProbeInterfaces;
1235 std::vector<std::shared_ptr<PerformProbe>> dbusProbePointers;
James Feist75fdeeb2018-02-20 14:26:16 -08001236
James Feist733f7652019-11-13 14:32:29 -08001237 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001238 {
James Feist733f7652019-11-13 14:32:29 -08001239 auto findProbe = it->find("Probe");
1240 auto findName = it->find("Name");
James Feist787c3c32019-11-07 14:42:58 -08001241
James Feist733f7652019-11-13 14:32:29 -08001242 nlohmann::json probeCommand;
1243 // check for poorly formatted fields, probe must be an array
1244 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001245 {
James Feist733f7652019-11-13 14:32:29 -08001246 std::cerr << "configuration file missing probe:\n " << *it << "\n";
1247 it = _configurations.erase(it);
1248 continue;
1249 }
1250 else if ((*findProbe).type() != nlohmann::json::value_t::array)
1251 {
1252 probeCommand = nlohmann::json::array();
1253 probeCommand.push_back(*findProbe);
1254 }
1255 else
1256 {
1257 probeCommand = *findProbe;
1258 }
James Feist3cb5fec2018-01-23 14:41:51 -08001259
James Feist733f7652019-11-13 14:32:29 -08001260 if (findName == it->end())
1261 {
1262 std::cerr << "configuration file missing name:\n " << *it << "\n";
1263 it = _configurations.erase(it);
1264 continue;
1265 }
1266 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001267
James Feist733f7652019-11-13 14:32:29 -08001268 if (std::find(passedProbes.begin(), passedProbes.end(), probeName) !=
1269 passedProbes.end())
1270 {
1271 it = _configurations.erase(it);
1272 continue;
1273 }
1274 nlohmann::json* recordPtr = &(*it);
James Feist1b2e2242018-01-30 13:45:19 -08001275
James Feist733f7652019-11-13 14:32:29 -08001276 // store reference to this to children to makes sure we don't get
1277 // destroyed too early
1278 auto thisRef = shared_from_this();
1279 auto probePointer = std::make_shared<PerformProbe>(
1280 probeCommand, thisRef,
1281 [&, recordPtr, probeName](FoundDeviceT& foundDevices) {
James Feist08a5b172019-08-28 14:47:47 -07001282 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001283
James Feist35f5e0e2020-03-16 14:02:27 -07001284 std::set<nlohmann::json> usedNames;
James Feist733f7652019-11-13 14:32:29 -08001285 passedProbes.push_back(probeName);
James Feist94219d12020-03-03 11:52:25 -08001286 std::list<size_t> indexes(foundDevices.size());
1287 std::iota(indexes.begin(), indexes.end(), 1);
James Feist8f2710a2018-05-09 17:18:55 -07001288
James Feist35f5e0e2020-03-16 14:02:27 -07001289 size_t indexIdx = probeName.find("$");
1290 bool hasTemplateName = (indexIdx != std::string::npos);
James Feist94219d12020-03-03 11:52:25 -08001291
1292 // copy over persisted configurations and make sure we remove
1293 // indexes that are already used
1294 for (auto itr = foundDevices.begin();
1295 itr != foundDevices.end();)
James Feist08a5b172019-08-28 14:47:47 -07001296 {
James Feist94219d12020-03-03 11:52:25 -08001297 std::string recordName = getRecordName(*itr, probeName);
James Feistf1b14142019-04-10 15:22:09 -07001298
James Feist08a5b172019-08-28 14:47:47 -07001299 auto fromLastJson = lastJson.find(recordName);
1300 if (fromLastJson != lastJson.end())
1301 {
James Feist02d2b932020-02-06 16:28:48 -08001302 auto findExposes = fromLastJson->find("Exposes");
1303 // delete nulls from any updates
1304 if (findExposes != fromLastJson->end())
1305 {
1306 auto copy = nlohmann::json::array();
1307 for (auto& expose : *findExposes)
1308 {
1309 if (expose.is_null())
1310 {
1311 continue;
1312 }
1313 copy.emplace_back(expose);
1314 }
1315 *findExposes = copy;
1316 }
1317
James Feist08a5b172019-08-28 14:47:47 -07001318 // keep user changes
1319 _systemConfiguration[recordName] = *fromLastJson;
James Feist899e17f2019-09-13 11:46:29 -07001320 _missingConfigurations.erase(recordName);
James Feist94219d12020-03-03 11:52:25 -08001321 itr = foundDevices.erase(itr);
James Feist35f5e0e2020-03-16 14:02:27 -07001322 if (hasTemplateName)
James Feist94219d12020-03-03 11:52:25 -08001323 {
1324 auto nameIt = fromLastJson->find("Name");
1325 if (nameIt == fromLastJson->end())
1326 {
1327 std::cerr << "Last JSON Illegal\n";
1328 continue;
1329 }
1330
1331 int index = std::stoi(
1332 nameIt->get<std::string>().substr(indexIdx),
1333 nullptr, 0);
James Feist35f5e0e2020-03-16 14:02:27 -07001334 usedNames.insert(nameIt.value());
James Feist94219d12020-03-03 11:52:25 -08001335 auto usedIt = std::find(indexes.begin(),
1336 indexes.end(), index);
1337
1338 if (usedIt == indexes.end())
1339 {
1340 continue; // less items now
1341 }
1342 indexes.erase(usedIt);
1343 }
1344
James Feist08a5b172019-08-28 14:47:47 -07001345 continue;
1346 }
James Feist94219d12020-03-03 11:52:25 -08001347 itr++;
1348 }
James Feist35f5e0e2020-03-16 14:02:27 -07001349
1350 std::optional<std::string> replaceStr;
1351
James Feist94219d12020-03-03 11:52:25 -08001352 for (auto& foundDevice : foundDevices)
1353 {
1354 nlohmann::json record = *recordPtr;
1355 std::string recordName =
1356 getRecordName(foundDevice, probeName);
1357 size_t foundDeviceIdx = indexes.front();
1358 indexes.pop_front();
James Feistf1b14142019-04-10 15:22:09 -07001359
James Feist35f5e0e2020-03-16 14:02:27 -07001360 // check name first so we have no duplicate names
1361 auto getName = record.find("Name");
1362 if (getName == record.end())
1363 {
1364 std::cerr << "Record Missing Name! " << record.dump();
1365 continue; // this should be impossible at this level
1366 }
1367
1368 nlohmann::json copyForName = {{"Name", getName.value()}};
1369 nlohmann::json::iterator copyIt = copyForName.begin();
1370 std::optional<std::string> replaceVal = templateCharReplace(
1371 copyIt, foundDevice, foundDeviceIdx, replaceStr);
1372
1373 if (!replaceStr && replaceVal)
1374 {
1375 if (usedNames.find(copyIt.value()) != usedNames.end())
1376 {
1377 replaceStr = replaceVal;
1378 copyForName = {{"Name", getName.value()}};
1379 copyIt = copyForName.begin();
1380 templateCharReplace(copyIt, foundDevice,
1381 foundDeviceIdx, replaceStr);
1382 }
1383 }
1384
1385 if (replaceStr)
1386 {
1387 std::cerr << "Duplicates found, replacing "
1388 << *replaceStr
1389 << " with found device index.\n Consider "
1390 "fixing template to not have duplicates\n";
1391 }
James Feist08a5b172019-08-28 14:47:47 -07001392
1393 for (auto keyPair = record.begin(); keyPair != record.end();
1394 keyPair++)
1395 {
James Feist35f5e0e2020-03-16 14:02:27 -07001396 if (keyPair.key() == "Name")
1397 {
1398 keyPair.value() = copyIt.value();
1399 usedNames.insert(copyIt.value());
1400
1401 continue; // already covered above
1402 }
James Feist08a5b172019-08-28 14:47:47 -07001403 templateCharReplace(keyPair, foundDevice,
James Feist35f5e0e2020-03-16 14:02:27 -07001404 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001405 }
1406
James Feist35f5e0e2020-03-16 14:02:27 -07001407 // insert into configuration temporarily to be able to
1408 // reference ourselves
1409
1410 _systemConfiguration[recordName] = record;
1411
James Feist08a5b172019-08-28 14:47:47 -07001412 auto findExpose = record.find("Exposes");
1413 if (findExpose == record.end())
1414 {
James Feistf1b14142019-04-10 15:22:09 -07001415 _systemConfiguration[recordName] = record;
James Feist08a5b172019-08-28 14:47:47 -07001416 continue;
1417 }
James Feistf1b14142019-04-10 15:22:09 -07001418
James Feist08a5b172019-08-28 14:47:47 -07001419 for (auto& expose : *findExpose)
1420 {
1421 for (auto keyPair = expose.begin();
1422 keyPair != expose.end(); keyPair++)
James Feistf1b14142019-04-10 15:22:09 -07001423 {
James Feist08a5b172019-08-28 14:47:47 -07001424
1425 templateCharReplace(keyPair, foundDevice,
James Feist35f5e0e2020-03-16 14:02:27 -07001426 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001427
James Feist668bbb12020-02-05 14:27:26 -08001428 bool isBind =
1429 boost::starts_with(keyPair.key(), "Bind");
1430 bool isDisable = keyPair.key() == "DisableNode";
1431
1432 // special cases
1433 if (!(isBind || isDisable))
James Feistf1b14142019-04-10 15:22:09 -07001434 {
James Feist668bbb12020-02-05 14:27:26 -08001435 continue;
1436 }
1437
1438 if (keyPair.value().type() !=
1439 nlohmann::json::value_t::string &&
1440 keyPair.value().type() !=
1441 nlohmann::json::value_t::array)
1442 {
1443 std::cerr << "Value is invalid type "
1444 << keyPair.key() << "\n";
1445 continue;
1446 }
1447
1448 std::vector<std::string> matches;
1449 if (keyPair.value().type() ==
1450 nlohmann::json::value_t::string)
1451 {
1452 matches.emplace_back(keyPair.value());
1453 }
1454 else
1455 {
1456 for (const auto& value : keyPair.value())
James Feistf1b14142019-04-10 15:22:09 -07001457 {
James Feist668bbb12020-02-05 14:27:26 -08001458 if (value.type() !=
1459 nlohmann::json::value_t::string)
1460 {
1461 std::cerr << "Value is invalid type "
1462 << value << "\n";
1463 break;
1464 }
1465 matches.emplace_back(value);
James Feistf1b14142019-04-10 15:22:09 -07001466 }
James Feist668bbb12020-02-05 14:27:26 -08001467 }
James Feist08a5b172019-08-28 14:47:47 -07001468
James Feist668bbb12020-02-05 14:27:26 -08001469 std::set<std::string> foundMatches;
1470 for (auto& configurationPair :
1471 _systemConfiguration.items())
1472 {
1473 if (isDisable)
James Feist8f2710a2018-05-09 17:18:55 -07001474 {
James Feist668bbb12020-02-05 14:27:26 -08001475 // don't disable ourselves
1476 if (configurationPair.key() == recordName)
James Feist3cb5fec2018-01-23 14:41:51 -08001477 {
James Feist1b2e2242018-01-30 13:45:19 -08001478 continue;
1479 }
James Feist668bbb12020-02-05 14:27:26 -08001480 }
1481 auto configListFind =
1482 configurationPair.value().find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001483
James Feist668bbb12020-02-05 14:27:26 -08001484 if (configListFind ==
1485 configurationPair.value().end() ||
1486 configListFind->type() !=
1487 nlohmann::json::value_t::array)
James Feist08a5b172019-08-28 14:47:47 -07001488 {
James Feist668bbb12020-02-05 14:27:26 -08001489 continue;
James Feist08a5b172019-08-28 14:47:47 -07001490 }
James Feist668bbb12020-02-05 14:27:26 -08001491 for (auto& exposedObject : *configListFind)
1492 {
1493 auto matchIt = std::find_if(
1494 matches.begin(), matches.end(),
1495 [name = (exposedObject)["Name"]
1496 .get<std::string>()](
1497 const std::string& s) {
1498 return s == name;
1499 });
1500 if (matchIt == matches.end())
1501 {
1502 continue;
1503 }
1504 foundMatches.insert(*matchIt);
1505
1506 if (isBind)
1507 {
1508 std::string bind = keyPair.key().substr(
1509 sizeof("Bind") - 1);
1510
1511 exposedObject["Status"] = "okay";
1512 expose[bind] = exposedObject;
1513 }
1514 else if (isDisable)
1515 {
1516 exposedObject["Status"] = "disabled";
1517 }
1518 }
1519 }
1520 if (foundMatches.size() != matches.size())
1521 {
1522 std::cerr << "configuration file "
1523 "dependency error, "
1524 "could not find "
1525 << keyPair.key() << " "
1526 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001527 }
1528 }
1529 }
James Feist08a5b172019-08-28 14:47:47 -07001530 // overwrite ourselves with cleaned up version
1531 _systemConfiguration[recordName] = record;
James Feist899e17f2019-09-13 11:46:29 -07001532 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001533 }
1534 });
James Feist787c3c32019-11-07 14:42:58 -08001535
James Feist733f7652019-11-13 14:32:29 -08001536 // parse out dbus probes by discarding other probe types, store in a
1537 // map
1538 for (const std::string& probe : probeCommand)
1539 {
1540 bool found = false;
1541 boost::container::flat_map<const char*, probe_type_codes,
1542 cmp_str>::const_iterator probeType;
1543 for (probeType = PROBE_TYPES.begin();
1544 probeType != PROBE_TYPES.end(); ++probeType)
James Feist787c3c32019-11-07 14:42:58 -08001545 {
James Feist733f7652019-11-13 14:32:29 -08001546 if (probe.find(probeType->first) != std::string::npos)
James Feist787c3c32019-11-07 14:42:58 -08001547 {
James Feist733f7652019-11-13 14:32:29 -08001548 found = true;
1549 break;
James Feist787c3c32019-11-07 14:42:58 -08001550 }
James Feist787c3c32019-11-07 14:42:58 -08001551 }
James Feist733f7652019-11-13 14:32:29 -08001552 if (found)
1553 {
1554 continue;
1555 }
1556 // syntax requires probe before first open brace
1557 auto findStart = probe.find("(");
1558 std::string interface = probe.substr(0, findStart);
1559 dbusProbeInterfaces.emplace(interface);
1560 dbusProbePointers.emplace_back(probePointer);
James Feist3cb5fec2018-01-23 14:41:51 -08001561 }
James Feist733f7652019-11-13 14:32:29 -08001562 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001563 }
James Feist75fdeeb2018-02-20 14:26:16 -08001564
James Feist4dc617b2020-05-01 09:54:47 -07001565 for (const std::string& interface : dbusProbeInterfaces)
1566 {
1567 registerCallback(_systemConfiguration, objServer, interface);
1568 }
1569
James Feist733f7652019-11-13 14:32:29 -08001570 // probe vector stores a shared_ptr to each PerformProbe that cares
1571 // about a dbus interface
James Feistb1728ca2020-04-30 15:40:55 -07001572 findDbusObjects(std::move(dbusProbePointers),
1573 std::move(dbusProbeInterfaces), shared_from_this());
1574 if constexpr (DEBUG)
1575 {
1576 std::cerr << __func__ << " " << __LINE__ << "\n";
1577 }
James Feist733f7652019-11-13 14:32:29 -08001578}
1579
1580PerformScan::~PerformScan()
1581{
1582 if (_passed)
James Feist75fdeeb2018-02-20 14:26:16 -08001583 {
James Feist733f7652019-11-13 14:32:29 -08001584 auto nextScan = std::make_shared<PerformScan>(
1585 _systemConfiguration, _missingConfigurations, _configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001586 objServer, std::move(_callback));
James Feist733f7652019-11-13 14:32:29 -08001587 nextScan->passedProbes = std::move(passedProbes);
1588 nextScan->dbusProbeObjects = std::move(dbusProbeObjects);
1589 nextScan->run();
James Feistb1728ca2020-04-30 15:40:55 -07001590
1591 if constexpr (DEBUG)
1592 {
1593 std::cerr << __func__ << " " << __LINE__ << "\n";
1594 }
James Feist8f2710a2018-05-09 17:18:55 -07001595 }
James Feist733f7652019-11-13 14:32:29 -08001596 else
1597 {
James Feist4dc617b2020-05-01 09:54:47 -07001598 _callback();
James Feistb1728ca2020-04-30 15:40:55 -07001599
1600 if constexpr (DEBUG)
1601 {
1602 std::cerr << __func__ << " " << __LINE__ << "\n";
1603 }
James Feist733f7652019-11-13 14:32:29 -08001604 }
1605}
James Feistc95cb142018-02-26 10:41:42 -08001606
James Feistb1728ca2020-04-30 15:40:55 -07001607void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001608 nlohmann::json& systemConfiguration)
1609{
1610 static bool scannedPowerOff = false;
1611 static bool scannedPowerOn = false;
1612
James Feistfb00f392019-06-25 14:16:48 -07001613 if (systemConfiguration.empty() || lastJson.empty())
1614 {
1615 return; // not ready yet
1616 }
James Feist1df06a42019-04-11 14:23:04 -07001617 if (scannedPowerOn)
1618 {
1619 return;
1620 }
1621
1622 if (!isPowerOn() && scannedPowerOff)
1623 {
1624 return;
1625 }
1626
James Feistb1728ca2020-04-30 15:40:55 -07001627 timer.expires_after(std::chrono::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001628 timer.async_wait(
1629 [&systemConfiguration](const boost::system::error_code& ec) {
1630 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001631 {
James Feist1a996582019-05-14 15:10:06 -07001632 // we were cancelled
1633 return;
1634 }
1635
1636 bool powerOff = !isPowerOn();
1637 for (const auto& item : lastJson.items())
1638 {
1639 if (systemConfiguration.find(item.key()) ==
1640 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001641 {
James Feist1a996582019-05-14 15:10:06 -07001642 bool isDetectedPowerOn = false;
1643 auto powerState = item.value().find("PowerState");
1644 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001645 {
James Feist1a996582019-05-14 15:10:06 -07001646 auto ptr = powerState->get_ptr<const std::string*>();
1647 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001648 {
James Feist1a996582019-05-14 15:10:06 -07001649 if (*ptr == "On" || *ptr == "BiosPost")
1650 {
1651 isDetectedPowerOn = true;
1652 }
James Feist1df06a42019-04-11 14:23:04 -07001653 }
1654 }
James Feist1a996582019-05-14 15:10:06 -07001655 if (powerOff && isDetectedPowerOn)
1656 {
1657 // power not on yet, don't know if it's there or not
1658 continue;
1659 }
1660 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1661 {
1662 // already logged it when power was off
1663 continue;
1664 }
James Feist1df06a42019-04-11 14:23:04 -07001665
James Feist1a996582019-05-14 15:10:06 -07001666 logDeviceRemoved(item.value());
1667 }
James Feist1df06a42019-04-11 14:23:04 -07001668 }
James Feist1a996582019-05-14 15:10:06 -07001669 scannedPowerOff = true;
1670 if (!powerOff)
1671 {
1672 scannedPowerOn = true;
1673 }
1674 });
James Feist1df06a42019-04-11 14:23:04 -07001675}
1676
James Feist8f2710a2018-05-09 17:18:55 -07001677// main properties changed entry
James Feist4dc617b2020-05-01 09:54:47 -07001678void propertiesChangedCallback(nlohmann::json& systemConfiguration,
1679 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001680{
James Feist2539ccd2020-05-01 16:15:08 -07001681 static bool inProgress = false;
James Feistb1728ca2020-04-30 15:40:55 -07001682 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001683 static size_t instance = 0;
1684 instance++;
1685 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001686
James Feistb1728ca2020-04-30 15:40:55 -07001687 timer.expires_after(std::chrono::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001688
1689 // setup an async wait as we normally get flooded with new requests
James Feist4dc617b2020-05-01 09:54:47 -07001690 timer.async_wait([&systemConfiguration, &objServer,
James Feist899e17f2019-09-13 11:46:29 -07001691 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001692 if (ec == boost::asio::error::operation_aborted)
1693 {
1694 // we were cancelled
1695 return;
1696 }
1697 else if (ec)
1698 {
1699 std::cerr << "async wait error " << ec << "\n";
1700 return;
1701 }
1702
James Feist2539ccd2020-05-01 16:15:08 -07001703 if (inProgress)
1704 {
1705 propertiesChangedCallback(systemConfiguration, objServer);
1706 return;
1707 }
1708 inProgress = true;
1709
James Feist8f2710a2018-05-09 17:18:55 -07001710 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001711 auto missingConfigurations = std::make_shared<nlohmann::json>();
1712 *missingConfigurations = systemConfiguration;
1713
James Feist8f2710a2018-05-09 17:18:55 -07001714 std::list<nlohmann::json> configurations;
1715 if (!findJsonFiles(configurations))
1716 {
1717 std::cerr << "cannot find json files\n";
James Feist2539ccd2020-05-01 16:15:08 -07001718 inProgress = false;
James Feist8f2710a2018-05-09 17:18:55 -07001719 return;
1720 }
1721
1722 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001723 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001724 objServer,
1725 [&systemConfiguration, &objServer, count, oldConfiguration,
1726 missingConfigurations]() {
James Feist899e17f2019-09-13 11:46:29 -07001727 // this is something that since ac has been applied to the bmc
1728 // we saw, and we no longer see it
1729 bool powerOff = !isPowerOn();
1730 for (const auto& item : missingConfigurations->items())
1731 {
1732 bool isDetectedPowerOn = false;
1733 auto powerState = item.value().find("PowerState");
1734 if (powerState != item.value().end())
1735 {
1736 auto ptr = powerState->get_ptr<const std::string*>();
1737 if (ptr)
1738 {
1739 if (*ptr == "On" || *ptr == "BiosPost")
1740 {
1741 isDetectedPowerOn = true;
1742 }
1743 }
1744 }
1745 if (powerOff && isDetectedPowerOn)
1746 {
1747 // power not on yet, don't know if it's there or not
1748 continue;
1749 }
1750 std::string name = item.value()["Name"].get<std::string>();
James Feist02d2b932020-02-06 16:28:48 -08001751 std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
James Feist899e17f2019-09-13 11:46:29 -07001752 ifaces = inventory[name];
1753 for (auto& iface : ifaces)
1754 {
James Feist02d2b932020-02-06 16:28:48 -08001755 auto sharedPtr = iface.lock();
1756 if (!sharedPtr)
1757 {
1758 continue; // was already deleted elsewhere
1759 }
1760 objServer.remove_interface(sharedPtr);
James Feist899e17f2019-09-13 11:46:29 -07001761 }
1762 ifaces.clear();
1763 systemConfiguration.erase(item.key());
1764 logDeviceRemoved(item.value());
1765 }
1766
James Feist8f2710a2018-05-09 17:18:55 -07001767 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001768 for (auto it = newConfiguration.begin();
1769 it != newConfiguration.end();)
1770 {
1771 auto findKey = oldConfiguration.find(it.key());
1772 if (findKey != oldConfiguration.end())
1773 {
1774 it = newConfiguration.erase(it);
1775 }
1776 else
1777 {
1778 it++;
1779 }
1780 }
James Feist899e17f2019-09-13 11:46:29 -07001781 for (const auto& item : newConfiguration.items())
1782 {
1783 logDeviceAdded(item.value());
1784 }
1785
James Feist2539ccd2020-05-01 16:15:08 -07001786 inProgress = false;
1787
James Feist8f2710a2018-05-09 17:18:55 -07001788 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001789 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001790
James Feistbb43d022018-06-12 15:44:33 -07001791 io.post([&]() {
1792 if (!writeJsonFiles(systemConfiguration))
1793 {
1794 std::cerr << "Error writing json files\n";
1795 }
1796 });
James Feist8f2710a2018-05-09 17:18:55 -07001797 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001798 postToDbus(newConfiguration, systemConfiguration,
1799 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001800 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001801 {
James Feist899e17f2019-09-13 11:46:29 -07001802 return;
James Feist1df06a42019-04-11 14:23:04 -07001803 }
James Feist899e17f2019-09-13 11:46:29 -07001804 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001805 });
1806 });
1807 });
1808 perfScan->run();
1809 });
James Feist75fdeeb2018-02-20 14:26:16 -08001810}
1811
James Feist4dc617b2020-05-01 09:54:47 -07001812void registerCallback(nlohmann::json& systemConfiguration,
1813 sdbusplus::asio::object_server& objServer,
1814 const std::string& interface)
James Feist75fdeeb2018-02-20 14:26:16 -08001815{
James Feist4dc617b2020-05-01 09:54:47 -07001816 static boost::container::flat_map<std::string, sdbusplus::bus::match::match>
1817 dbusMatches;
James Feist75fdeeb2018-02-20 14:26:16 -08001818
James Feist4dc617b2020-05-01 09:54:47 -07001819 auto find = dbusMatches.find(interface);
1820 if (find != dbusMatches.end())
James Feist75fdeeb2018-02-20 14:26:16 -08001821 {
James Feist4dc617b2020-05-01 09:54:47 -07001822 return;
James Feist75fdeeb2018-02-20 14:26:16 -08001823 }
James Feist4dc617b2020-05-01 09:54:47 -07001824 std::function<void(sdbusplus::message::message & message)> eventHandler =
1825
1826 [&](sdbusplus::message::message&) {
1827 propertiesChangedCallback(systemConfiguration, objServer);
1828 };
1829
1830 sdbusplus::bus::match::match match(
1831 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
1832 "type='signal',member='PropertiesChanged',arg0='" + interface + "'",
1833 eventHandler);
1834 dbusMatches.emplace(interface, std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001835}
1836
James Feist98132792019-07-09 13:29:09 -07001837int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001838{
1839 // setup connection to dbus
James Feist8f2710a2018-05-09 17:18:55 -07001840 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001841 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001842
James Feist8f2710a2018-05-09 17:18:55 -07001843 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001844
James Feist8f2710a2018-05-09 17:18:55 -07001845 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1846 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1847 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001848
James Feist4131aea2018-03-09 09:47:30 -08001849 // to keep reference to the match / filter objects so they don't get
1850 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001851
1852 nlohmann::json systemConfiguration = nlohmann::json::object();
1853
James Feist4dc617b2020-05-01 09:54:47 -07001854 io.post(
1855 [&]() { propertiesChangedCallback(systemConfiguration, objServer); });
James Feist4131aea2018-03-09 09:47:30 -08001856
James Feistfd1264a2018-05-03 12:10:00 -07001857 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -07001858 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001859 });
James Feist8f2710a2018-05-09 17:18:55 -07001860 entityIface->initialize();
1861
James Feist1df06a42019-04-11 14:23:04 -07001862 if (fwVersionIsSame())
1863 {
1864 if (std::filesystem::is_regular_file(currentConfiguration))
1865 {
1866 // this file could just be deleted, but it's nice for debug
1867 std::filesystem::create_directory(tempConfigDir);
1868 std::filesystem::remove(lastConfiguration);
1869 std::filesystem::copy(currentConfiguration, lastConfiguration);
1870 std::filesystem::remove(currentConfiguration);
1871
1872 std::ifstream jsonStream(lastConfiguration);
1873 if (jsonStream.good())
1874 {
1875 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1876 if (data.is_discarded())
1877 {
1878 std::cerr << "syntax error in " << lastConfiguration
1879 << "\n";
1880 }
1881 else
1882 {
1883 lastJson = std::move(data);
1884 }
1885 }
1886 else
1887 {
1888 std::cerr << "unable to open " << lastConfiguration << "\n";
1889 }
1890 }
1891 }
1892 else
1893 {
1894 // not an error, just logging at this level to make it in the journal
1895 std::cerr << "Clearing previous configuration\n";
1896 std::filesystem::remove(currentConfiguration);
1897 }
1898
1899 // some boards only show up after power is on, we want to not say they are
1900 // removed until the same state happens
1901 setupPowerMatch(SYSTEM_BUS);
1902
James Feist1b2e2242018-01-30 13:45:19 -08001903 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001904
1905 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001906}