blob: 8b3dc4d4ff1d2b6a5978e7622d2e831130d0119d [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 Feist733f7652019-11-13 14:32:29 -0800188 std::shared_ptr<PerformScan> scan)
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 Feist733f7652019-11-13 14:32:29 -0800203 scan](boost::system::error_code& ec,
204 const GetSubTreeType& interfaceSubtree) {
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
216 // if we can't communicate to the mapper something is very wrong
217 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700218 }
James Feist787c3c32019-11-07 14:42:58 -0800219
James Feistb1728ca2020-04-30 15:40:55 -0700220 for (const auto& [path, object] : interfaceSubtree)
James Feist3cb5fec2018-01-23 14:41:51 -0800221 {
James Feistb1728ca2020-04-30 15:40:55 -0700222 for (const auto& [busname, ifaces] : object)
James Feist8f2710a2018-05-09 17:18:55 -0700223 {
James Feistb1728ca2020-04-30 15:40:55 -0700224 for (const std::string& iface : ifaces)
225 {
226 auto ifaceObjFind = interfaces.find(iface);
227
228 if (ifaceObjFind != interfaces.end())
229 {
230 interfaceConnections.emplace(busname, path, iface);
231 }
232 }
James Feist8f2710a2018-05-09 17:18:55 -0700233 }
James Feist3cb5fec2018-01-23 14:41:51 -0800234 }
James Feist787c3c32019-11-07 14:42:58 -0800235
James Feist63845bf2019-01-24 12:19:51 -0800236 if (interfaceConnections.empty())
237 {
James Feist63845bf2019-01-24 12:19:51 -0800238 return;
239 }
James Feist787c3c32019-11-07 14:42:58 -0800240
James Feistb1728ca2020-04-30 15:40:55 -0700241 for (const auto& call : interfaceConnections)
242 {
243 getInterfaces(call, probeVector, scan);
James Feist8f2710a2018-05-09 17:18:55 -0700244 }
245 },
246 "xyz.openbmc_project.ObjectMapper",
247 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700248 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist787c3c32019-11-07 14:42:58 -0800249 interfaces);
James Feistb1728ca2020-04-30 15:40:55 -0700250
251 if constexpr (DEBUG)
252 {
253 std::cerr << __func__ << " " << __LINE__ << "\n";
254 }
James Feist3cb5fec2018-01-23 14:41:51 -0800255}
James Feistb1728ca2020-04-30 15:40:55 -0700256
James Feist8f2710a2018-05-09 17:18:55 -0700257// probes dbus interface dictionary for a key with a value that matches a regex
James Feist08a5b172019-08-28 14:47:47 -0700258bool probeDbus(const std::string& interface,
259 const std::map<std::string, nlohmann::json>& matches,
James Feist733f7652019-11-13 14:32:29 -0800260 FoundDeviceT& devices, std::shared_ptr<PerformScan> scan,
261 bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800262{
James Feista465ccc2019-02-08 12:51:01 -0800263 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
James Feist733f7652019-11-13 14:32:29 -0800264 dbusObject = scan->dbusProbeObjects[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800265 if (dbusObject.empty())
266 {
James Feist8f2710a2018-05-09 17:18:55 -0700267 foundProbe = false;
268 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800269 }
270 foundProbe = true;
271
272 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800273 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800274 {
275 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800276 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800277 {
278 auto deviceValue = device.find(match.first);
279 if (deviceValue != device.end())
280 {
Brad Bishop3cb8a602020-08-25 17:40:54 -0400281 deviceMatches = matchProbe(match.second, deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800282 }
283 else
284 {
285 deviceMatches = false;
286 break;
287 }
288 }
289 if (deviceMatches)
290 {
James Feistf5125b02019-06-06 11:27:43 -0700291 devices.emplace_back(device);
James Feist3cb5fec2018-01-23 14:41:51 -0800292 foundMatch = true;
293 deviceMatches = false; // for next iteration
294 }
295 }
296 return foundMatch;
297}
298
299// default probe entry point, iterates a list looking for specific types to
300// call specific probe functions
301bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800302 const std::vector<std::string>& probeCommand,
James Feist733f7652019-11-13 14:32:29 -0800303 std::shared_ptr<PerformScan> scan,
James Feist08a5b172019-08-28 14:47:47 -0700304 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
305 foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800306{
307 const static std::regex command(R"(\((.*)\))");
308 std::smatch match;
309 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700310 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800311 bool cur = true;
312 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
James Feist54a0dca2019-06-26 10:34:54 -0700313 bool first = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800314
James Feista465ccc2019-02-08 12:51:01 -0800315 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800316 {
317 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800318 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800319 cmp_str>::const_iterator probeType;
320
321 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
Patrick Venture4b7a7452019-10-28 08:41:02 -0700322 ++probeType)
James Feist3cb5fec2018-01-23 14:41:51 -0800323 {
324 if (probe.find(probeType->first) != std::string::npos)
325 {
326 foundProbe = true;
327 break;
328 }
329 }
330 if (foundProbe)
331 {
332 switch (probeType->second)
333 {
James Feist9eb0b582018-04-27 12:15:46 -0700334 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800335 {
James Feiste31e00a2019-07-24 10:45:43 -0700336 cur = false;
337 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800338 }
James Feist9eb0b582018-04-27 12:15:46 -0700339 case probe_type_codes::TRUE_T:
340 {
James Feiste31e00a2019-07-24 10:45:43 -0700341 cur = true;
342 break;
James Feist9eb0b582018-04-27 12:15:46 -0700343 }
344 case probe_type_codes::MATCH_ONE:
345 {
346 // set current value to last, this probe type shouldn't
347 // affect the outcome
348 cur = ret;
349 matchOne = true;
350 break;
351 }
352 /*case probe_type_codes::AND:
353 break;
354 case probe_type_codes::OR:
355 break;
356 // these are no-ops until the last command switch
357 */
358 case probe_type_codes::FOUND:
359 {
360 if (!std::regex_search(probe, match, command))
361 {
James Feist0eb40352019-04-09 14:44:04 -0700362 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700363 << "\n";
364 return false;
365 }
366 std::string commandStr = *(match.begin() + 1);
367 boost::replace_all(commandStr, "'", "");
James Feist733f7652019-11-13 14:32:29 -0800368 cur = (std::find(scan->passedProbes.begin(),
369 scan->passedProbes.end(),
370 commandStr) != scan->passedProbes.end());
James Feist9eb0b582018-04-27 12:15:46 -0700371 break;
372 }
James Feist0eb40352019-04-09 14:44:04 -0700373 default:
374 {
375 break;
376 }
James Feist3cb5fec2018-01-23 14:41:51 -0800377 }
378 }
379 // look on dbus for object
380 else
381 {
382 if (!std::regex_search(probe, match, command))
383 {
James Feist0eb40352019-04-09 14:44:04 -0700384 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800385 return false;
386 }
387 std::string commandStr = *(match.begin() + 1);
388 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700389 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800390 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800391 auto json = nlohmann::json::parse(commandStr, nullptr, false);
392 if (json.is_discarded())
393 {
James Feist0eb40352019-04-09 14:44:04 -0700394 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800395 return false;
396 }
397 // we can match any (string, variant) property. (string, string)
398 // does a regex
399 std::map<std::string, nlohmann::json> dbusProbeMap =
400 json.get<std::map<std::string, nlohmann::json>>();
401 auto findStart = probe.find("(");
402 if (findStart == std::string::npos)
403 {
404 return false;
405 }
406 std::string probeInterface = probe.substr(0, findStart);
James Feist733f7652019-11-13 14:32:29 -0800407 cur = probeDbus(probeInterface, dbusProbeMap, foundDevs, scan,
408 foundProbe);
James Feist3cb5fec2018-01-23 14:41:51 -0800409 }
410
411 // some functions like AND and OR only take affect after the
412 // fact
James Feist54a0dca2019-06-26 10:34:54 -0700413 if (lastCommand == probe_type_codes::AND)
James Feist3cb5fec2018-01-23 14:41:51 -0800414 {
James Feist54a0dca2019-06-26 10:34:54 -0700415 ret = cur && ret;
416 }
417 else if (lastCommand == probe_type_codes::OR)
418 {
419 ret = cur || ret;
420 }
421
422 if (first)
423 {
424 ret = cur;
425 first = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800426 }
427 lastCommand = probeType != PROBE_TYPES.end()
428 ? probeType->second
429 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800430 }
431
432 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800433 if (ret && foundDevs.size() == 0)
434 {
James Feist08a5b172019-08-28 14:47:47 -0700435 foundDevs.emplace_back(
436 boost::container::flat_map<std::string, BasicVariantType>{});
James Feist3cb5fec2018-01-23 14:41:51 -0800437 }
James Feist0eb40352019-04-09 14:44:04 -0700438 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700439 {
James Feist71f295f2019-06-20 13:35:12 -0700440 // match the last one
441 auto last = foundDevs.back();
James Feist0eb40352019-04-09 14:44:04 -0700442 foundDevs.clear();
James Feist71f295f2019-06-20 13:35:12 -0700443
444 foundDevs.emplace_back(std::move(last));
James Feist6bd2a022018-03-13 12:30:58 -0700445 }
James Feist3cb5fec2018-01-23 14:41:51 -0800446 return ret;
447}
448
James Feist7d807752019-11-13 14:47:57 -0800449PerformProbe::PerformProbe(const std::vector<std::string>& probeCommand,
450 std::shared_ptr<PerformScan>& scanPtr,
451 std::function<void(FoundDeviceT&)>&& callback) :
452 _probeCommand(probeCommand),
453 scan(scanPtr), _callback(std::move(callback))
James Feist8c505da2020-05-28 10:06:33 -0700454{}
James Feist7d807752019-11-13 14:47:57 -0800455PerformProbe::~PerformProbe()
456{
457 FoundDeviceT foundDevs;
458 if (probe(_probeCommand, scan, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700459 {
James Feist7d807752019-11-13 14:47:57 -0800460 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700461 }
James Feist7d807752019-11-13 14:47:57 -0800462}
James Feist8f2710a2018-05-09 17:18:55 -0700463
464// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800465bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800466{
James Feist1df06a42019-04-11 14:23:04 -0700467 std::filesystem::create_directory(configurationOutDir);
468 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700469 if (!output.good())
470 {
471 return false;
472 }
James Feist1b2e2242018-01-30 13:45:19 -0800473 output << systemConfiguration.dump(4);
474 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700475 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700476}
James Feist1b2e2242018-01-30 13:45:19 -0800477
James Feist97a63f12018-05-17 13:50:57 -0700478template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800479bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
480 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700481{
482 try
483 {
484 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800485 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700486 ref = value;
487 return true;
488 }
James Feist98132792019-07-09 13:29:09 -0700489 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700490 {
491 return false;
492 }
493}
James Feistbb43d022018-06-12 15:44:33 -0700494
James Feistebcc26b2019-03-22 12:30:43 -0700495// template function to add array as dbus property
496template <typename PropertyType>
497void addArrayToDbus(const std::string& name, const nlohmann::json& array,
498 sdbusplus::asio::dbus_interface* iface,
499 sdbusplus::asio::PropertyPermission permission,
500 nlohmann::json& systemConfiguration,
501 const std::string& jsonPointerString)
502{
503 std::vector<PropertyType> values;
504 for (const auto& property : array)
505 {
506 auto ptr = property.get_ptr<const PropertyType*>();
507 if (ptr != nullptr)
508 {
509 values.emplace_back(*ptr);
510 }
511 }
512
513 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
514 {
515 iface->register_property(name, values);
516 }
517 else
518 {
519 iface->register_property(
520 name, values,
521 [&systemConfiguration,
522 jsonPointerString{std::string(jsonPointerString)}](
523 const std::vector<PropertyType>& newVal,
524 std::vector<PropertyType>& val) {
525 val = newVal;
526 if (!setJsonFromPointer(jsonPointerString, val,
527 systemConfiguration))
528 {
529 std::cerr << "error setting json field\n";
530 return -1;
531 }
532 if (!writeJsonFiles(systemConfiguration))
533 {
534 std::cerr << "error setting json file\n";
535 return -1;
536 }
537 return 1;
538 });
539 }
540}
541
James Feistbb43d022018-06-12 15:44:33 -0700542template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800543void addProperty(const std::string& propertyName, const PropertyType& value,
544 sdbusplus::asio::dbus_interface* iface,
545 nlohmann::json& systemConfiguration,
546 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700547 sdbusplus::asio::PropertyPermission permission)
548{
549 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
550 {
551 iface->register_property(propertyName, value);
552 return;
553 }
James Feist68500ff2018-08-08 15:40:42 -0700554 iface->register_property(
555 propertyName, value,
556 [&systemConfiguration,
557 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800558 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700559 val = newVal;
560 if (!setJsonFromPointer(jsonPointerString, val,
561 systemConfiguration))
562 {
563 std::cerr << "error setting json field\n";
564 return -1;
565 }
James Feistc6248a52018-08-14 10:09:45 -0700566 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700567 {
568 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700569 return -1;
570 }
571 return 1;
572 });
573}
574
575void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800576 const std::string& jsonPointerPath,
577 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
578 sdbusplus::asio::object_server& objServer,
579 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700580{
581 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
582 iface->register_method(
583 "Delete", [&objServer, &systemConfiguration, interface,
584 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700585 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700586 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700587 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700588 {
589 // this technically can't happen as the pointer is pointing to
590 // us
591 throw DBusInternalError();
592 }
593 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feistc6248a52018-08-14 10:09:45 -0700594 systemConfiguration[ptr] = nullptr;
595
James Feist02d2b932020-02-06 16:28:48 -0800596 // todo(james): dig through sdbusplus to find out why we can't
597 // delete it in a method call
598 io.post([&objServer, dbusInterface]() mutable {
599 objServer.remove_interface(dbusInterface);
600 });
601
James Feistc6248a52018-08-14 10:09:45 -0700602 if (!writeJsonFiles(systemConfiguration))
603 {
604 std::cerr << "error setting json file\n";
605 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700606 }
James Feist68500ff2018-08-08 15:40:42 -0700607 });
James Feistbb43d022018-06-12 15:44:33 -0700608}
609
James Feist1b2e2242018-01-30 13:45:19 -0800610// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700611void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800612 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
613 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
614 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700615 sdbusplus::asio::PropertyPermission permission =
616 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800617{
James Feista465ccc2019-02-08 12:51:01 -0800618 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800619 {
James Feist8f2710a2018-05-09 17:18:55 -0700620 auto type = dictPair.value().type();
621 bool array = false;
622 if (dictPair.value().type() == nlohmann::json::value_t::array)
623 {
624 array = true;
625 if (!dictPair.value().size())
626 {
627 continue;
628 }
629 type = dictPair.value()[0].type();
630 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800631 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700632 {
633 if (arrayItem.type() != type)
634 {
635 isLegal = false;
636 break;
637 }
638 }
639 if (!isLegal)
640 {
641 std::cerr << "dbus format error" << dictPair.value() << "\n";
642 continue;
643 }
James Feista218ddb2019-04-11 14:01:31 -0700644 }
645 if (type == nlohmann::json::value_t::object)
646 {
647 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700648 }
James Feist97a63f12018-05-17 13:50:57 -0700649 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700650 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
651 {
652 // all setable numbers are doubles as it is difficult to always
653 // create a configuration file with all whole numbers as decimals
654 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700655 if (array)
656 {
657 if (dictPair.value()[0].is_number())
658 {
659 type = nlohmann::json::value_t::number_float;
660 }
661 }
662 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700663 {
664 type = nlohmann::json::value_t::number_float;
665 }
666 }
667
James Feist8f2710a2018-05-09 17:18:55 -0700668 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800669 {
James Feist9eb0b582018-04-27 12:15:46 -0700670 case (nlohmann::json::value_t::boolean):
671 {
James Feist8f2710a2018-05-09 17:18:55 -0700672 if (array)
673 {
674 // todo: array of bool isn't detected correctly by
675 // sdbusplus, change it to numbers
676 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700677 iface.get(), permission,
678 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700679 }
James Feistbb43d022018-06-12 15:44:33 -0700680
James Feist97a63f12018-05-17 13:50:57 -0700681 else
682 {
James Feistbb43d022018-06-12 15:44:33 -0700683 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700684 iface.get(), systemConfiguration, key,
685 permission);
James Feist97a63f12018-05-17 13:50:57 -0700686 }
James Feist9eb0b582018-04-27 12:15:46 -0700687 break;
688 }
689 case (nlohmann::json::value_t::number_integer):
690 {
James Feist8f2710a2018-05-09 17:18:55 -0700691 if (array)
692 {
693 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700694 iface.get(), permission,
695 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700696 }
697 else
698 {
James Feistbb43d022018-06-12 15:44:33 -0700699 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700700 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700701 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700702 }
James Feist9eb0b582018-04-27 12:15:46 -0700703 break;
704 }
705 case (nlohmann::json::value_t::number_unsigned):
706 {
James Feist8f2710a2018-05-09 17:18:55 -0700707 if (array)
708 {
709 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700710 iface.get(), permission,
711 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700712 }
713 else
714 {
James Feistbb43d022018-06-12 15:44:33 -0700715 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700716 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700717 systemConfiguration, key,
718 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_float):
723 {
James Feist8f2710a2018-05-09 17:18:55 -0700724 if (array)
725 {
726 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700727 iface.get(), permission,
728 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700729 }
James Feistbb43d022018-06-12 15:44:33 -0700730
James Feist97a63f12018-05-17 13:50:57 -0700731 else
732 {
James Feistbb43d022018-06-12 15:44:33 -0700733 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700734 iface.get(), systemConfiguration, key,
735 permission);
James Feist97a63f12018-05-17 13:50:57 -0700736 }
James Feist9eb0b582018-04-27 12:15:46 -0700737 break;
738 }
739 case (nlohmann::json::value_t::string):
740 {
James Feist8f2710a2018-05-09 17:18:55 -0700741 if (array)
742 {
James Feistebcc26b2019-03-22 12:30:43 -0700743 addArrayToDbus<std::string>(
744 dictPair.key(), dictPair.value(), iface.get(),
745 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700746 }
747 else
748 {
James Feistc6248a52018-08-14 10:09:45 -0700749 addProperty(
750 dictPair.key(), dictPair.value().get<std::string>(),
751 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700752 }
James Feist9eb0b582018-04-27 12:15:46 -0700753 break;
754 }
James Feist0eb40352019-04-09 14:44:04 -0700755 default:
756 {
James Feista218ddb2019-04-11 14:01:31 -0700757 std::cerr << "Unexpected json type in system configuration "
758 << dictPair.key() << ": "
759 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700760 break;
761 }
James Feist1b2e2242018-01-30 13:45:19 -0800762 }
763 }
James Feistc6248a52018-08-14 10:09:45 -0700764 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
765 {
766 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
767 systemConfiguration);
768 }
James Feist8f2710a2018-05-09 17:18:55 -0700769 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800770}
771
James Feista465ccc2019-02-08 12:51:01 -0800772sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700773{
774 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
775 interface) != settableInterfaces.end()
776 ? sdbusplus::asio::PropertyPermission::readWrite
777 : sdbusplus::asio::PropertyPermission::readOnly;
778}
779
James Feista465ccc2019-02-08 12:51:01 -0800780void createAddObjectMethod(const std::string& jsonPointerPath,
781 const std::string& path,
782 nlohmann::json& systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700783 sdbusplus::asio::object_server& objServer,
784 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700785{
James Feistd58879a2019-09-11 11:26:07 -0700786 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
787 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700788
789 iface->register_method(
790 "AddObject",
791 [&systemConfiguration, &objServer,
James Feistd58879a2019-09-11 11:26:07 -0700792 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
793 board](const boost::container::flat_map<std::string, JsonVariantType>&
794 data) {
James Feist68500ff2018-08-08 15:40:42 -0700795 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800796 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700797 auto findExposes = base.find("Exposes");
798
799 if (findExposes == base.end())
800 {
801 throw std::invalid_argument("Entity must have children.");
802 }
803
804 // this will throw invalid-argument to sdbusplus if invalid json
805 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800806 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700807 {
James Feista465ccc2019-02-08 12:51:01 -0800808 nlohmann::json& newJson = newData[item.first];
809 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
810 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700811 }
812
813 auto findName = newData.find("Name");
814 auto findType = newData.find("Type");
815 if (findName == newData.end() || findType == newData.end())
816 {
817 throw std::invalid_argument("AddObject missing Name or Type");
818 }
James Feista465ccc2019-02-08 12:51:01 -0800819 const std::string* type = findType->get_ptr<const std::string*>();
820 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700821 if (type == nullptr || name == nullptr)
822 {
823 throw std::invalid_argument("Type and Name must be a string.");
824 }
825
James Feist02d2b932020-02-06 16:28:48 -0800826 bool foundNull = false;
James Feist68500ff2018-08-08 15:40:42 -0700827 size_t lastIndex = 0;
828 // we add in the "exposes"
James Feist02d2b932020-02-06 16:28:48 -0800829 for (const auto& expose : *findExposes)
James Feist68500ff2018-08-08 15:40:42 -0700830 {
James Feist02d2b932020-02-06 16:28:48 -0800831 if (expose.is_null())
832 {
833 foundNull = true;
834 continue;
835 }
836
837 if (expose["Name"] == *name && expose["Type"] == *type)
James Feist68500ff2018-08-08 15:40:42 -0700838 {
839 throw std::invalid_argument(
840 "Field already in JSON, not adding");
841 }
James Feist02d2b932020-02-06 16:28:48 -0800842
843 if (foundNull)
844 {
845 continue;
846 }
847
James Feist68500ff2018-08-08 15:40:42 -0700848 lastIndex++;
849 }
850
851 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
852 *type + ".json");
853 // todo(james) we might want to also make a list of 'can add'
854 // interfaces but for now I think the assumption if there is a
855 // schema avaliable that it is allowed to update is fine
856 if (!schemaFile.good())
857 {
858 throw std::invalid_argument(
859 "No schema avaliable, cannot validate.");
860 }
861 nlohmann::json schema =
862 nlohmann::json::parse(schemaFile, nullptr, false);
863 if (schema.is_discarded())
864 {
865 std::cerr << "Schema not legal" << *type << ".json\n";
866 throw DBusInternalError();
867 }
868 if (!validateJson(schema, newData))
869 {
870 throw std::invalid_argument("Data does not match schema");
871 }
James Feist02d2b932020-02-06 16:28:48 -0800872 if (foundNull)
873 {
874 findExposes->at(lastIndex) = newData;
875 }
876 else
877 {
878 findExposes->push_back(newData);
879 }
James Feist68500ff2018-08-08 15:40:42 -0700880 if (!writeJsonFiles(systemConfiguration))
881 {
882 std::cerr << "Error writing json files\n";
883 throw DBusInternalError();
884 }
885 std::string dbusName = *name;
886
887 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700888 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistd58879a2019-09-11 11:26:07 -0700889
890 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
891 createInterface(objServer, path + "/" + dbusName,
892 "xyz.openbmc_project.Configuration." + *type,
James Feist02d2b932020-02-06 16:28:48 -0800893 board, true);
James Feist68500ff2018-08-08 15:40:42 -0700894 // permission is read-write, as since we just created it, must be
895 // runtime modifiable
896 populateInterfaceFromJson(
897 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700898 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700899 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700900 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700901 });
902 iface->initialize();
903}
904
James Feista465ccc2019-02-08 12:51:01 -0800905void postToDbus(const nlohmann::json& newConfiguration,
906 nlohmann::json& systemConfiguration,
907 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800908
James Feist1b2e2242018-01-30 13:45:19 -0800909{
James Feist97a63f12018-05-17 13:50:57 -0700910 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800911 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800912 {
James Feistf1b14142019-04-10 15:22:09 -0700913 std::string boardKey = boardPair.value()["Name"];
James Feistd58879a2019-09-11 11:26:07 -0700914 std::string boardKeyOrig = boardPair.value()["Name"];
James Feistf1b14142019-04-10 15:22:09 -0700915 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700916 // loop through newConfiguration, but use values from system
917 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700918 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700919 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800920 std::string boardType;
921 if (findBoardType != boardValues.end() &&
922 findBoardType->type() == nlohmann::json::value_t::string)
923 {
924 boardType = findBoardType->get<std::string>();
925 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700926 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800927 }
928 else
929 {
930 std::cerr << "Unable to find type for " << boardKey
931 << " reverting to Chassis.\n";
932 boardType = "Chassis";
933 }
James Feist11be6672018-04-06 14:05:32 -0700934 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800935
936 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700937 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700938 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
939 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800940
James Feistd58879a2019-09-11 11:26:07 -0700941 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
942 createInterface(objServer, boardName,
943 "xyz.openbmc_project.Inventory.Item", boardKey);
James Feist68500ff2018-08-08 15:40:42 -0700944
James Feistd58879a2019-09-11 11:26:07 -0700945 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
946 createInterface(objServer, boardName,
947 "xyz.openbmc_project.Inventory.Item." + boardType,
948 boardKeyOrig);
James Feist11be6672018-04-06 14:05:32 -0700949
James Feist68500ff2018-08-08 15:40:42 -0700950 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700951 objServer, boardKeyOrig);
James Feist68500ff2018-08-08 15:40:42 -0700952
James Feist97a63f12018-05-17 13:50:57 -0700953 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700954 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700955 jsonPointerPath += "/";
956 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800957 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700958 {
959 if (boardField.value().type() == nlohmann::json::value_t::object)
960 {
James Feistd58879a2019-09-11 11:26:07 -0700961 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
962 createInterface(objServer, boardName, boardField.key(),
963 boardKeyOrig);
964
James Feistc6248a52018-08-14 10:09:45 -0700965 populateInterfaceFromJson(systemConfiguration,
966 jsonPointerPath + boardField.key(),
967 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700968 }
969 }
James Feist97a63f12018-05-17 13:50:57 -0700970
James Feist1e3e6982018-08-03 16:09:28 -0700971 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800972 if (exposes == boardValues.end())
973 {
974 continue;
975 }
James Feist97a63f12018-05-17 13:50:57 -0700976 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700977 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700978
979 // store the board level pointer so we can modify it on the way down
980 std::string jsonPointerPathBoard = jsonPointerPath;
981 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -0800982 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -0800983 {
James Feist97a63f12018-05-17 13:50:57 -0700984 exposesIndex++;
985 jsonPointerPath = jsonPointerPathBoard;
986 jsonPointerPath += std::to_string(exposesIndex);
987
James Feistd63d18a2018-07-19 15:23:45 -0700988 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800989 if (findName == item.end())
990 {
991 std::cerr << "cannot find name in field " << item << "\n";
992 continue;
993 }
James Feist1e3e6982018-08-03 16:09:28 -0700994 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -0800995 // if status is not found it is assumed to be status = 'okay'
996 if (findStatus != item.end())
997 {
998 if (*findStatus == "disabled")
999 {
1000 continue;
1001 }
1002 }
James Feistd63d18a2018-07-19 15:23:45 -07001003 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001004 std::string itemType;
1005 if (findType != item.end())
1006 {
1007 itemType = findType->get<std::string>();
1008 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001009 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1010 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001011 }
1012 else
1013 {
1014 itemType = "unknown";
1015 }
1016 std::string itemName = findName->get<std::string>();
1017 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001018 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001019
James Feistd58879a2019-09-11 11:26:07 -07001020 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
1021 createInterface(objServer, boardName + "/" + itemName,
1022 "xyz.openbmc_project.Configuration." + itemType,
1023 boardKeyOrig);
James Feist1b2e2242018-01-30 13:45:19 -08001024
James Feist97a63f12018-05-17 13:50:57 -07001025 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001026 itemIface, item, objServer,
1027 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001028
James Feista465ccc2019-02-08 12:51:01 -08001029 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001030 {
James Feist97a63f12018-05-17 13:50:57 -07001031 jsonPointerPath = jsonPointerPathBoard +
1032 std::to_string(exposesIndex) + "/" +
1033 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001034 if (objectPair.value().type() ==
1035 nlohmann::json::value_t::object)
1036 {
James Feistd58879a2019-09-11 11:26:07 -07001037 std::shared_ptr<sdbusplus::asio::dbus_interface>
1038 objectIface = createInterface(
1039 objServer, boardName + "/" + itemName,
1040 "xyz.openbmc_project.Configuration." + itemType +
1041 "." + objectPair.key(),
1042 boardKeyOrig);
James Feist97a63f12018-05-17 13:50:57 -07001043
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +02001044 populateInterfaceFromJson(systemConfiguration,
1045 jsonPointerPath, objectIface,
1046 objectPair.value(), objServer,
1047 getPermission(objectPair.key()));
James Feist1b2e2242018-01-30 13:45:19 -08001048 }
1049 else if (objectPair.value().type() ==
1050 nlohmann::json::value_t::array)
1051 {
1052 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001053 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001054 {
James Feist8f2710a2018-05-09 17:18:55 -07001055 continue;
1056 }
1057 bool isLegal = true;
1058 auto type = objectPair.value()[0].type();
1059 if (type != nlohmann::json::value_t::object)
1060 {
1061 continue;
1062 }
1063
1064 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001065 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001066 {
1067 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001068 {
James Feist8f2710a2018-05-09 17:18:55 -07001069 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001070 break;
1071 }
James Feist8f2710a2018-05-09 17:18:55 -07001072 }
1073 if (!isLegal)
1074 {
1075 std::cerr << "dbus format error" << objectPair.value()
1076 << "\n";
1077 break;
1078 }
1079
James Feista465ccc2019-02-08 12:51:01 -08001080 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001081 {
James Feist97a63f12018-05-17 13:50:57 -07001082
James Feistd58879a2019-09-11 11:26:07 -07001083 std::shared_ptr<sdbusplus::asio::dbus_interface>
1084 objectIface = createInterface(
1085 objServer, boardName + "/" + itemName,
1086 "xyz.openbmc_project.Configuration." +
1087 itemType + "." + objectPair.key() +
1088 std::to_string(index),
1089 boardKeyOrig);
1090
James Feistc6248a52018-08-14 10:09:45 -07001091 populateInterfaceFromJson(
1092 systemConfiguration,
1093 jsonPointerPath + "/" + std::to_string(index),
1094 objectIface, arrayItem, objServer,
1095 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001096 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001097 }
1098 }
1099 }
1100 }
1101 }
1102}
1103
James Feist8f2710a2018-05-09 17:18:55 -07001104// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001105bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001106{
1107 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001108 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001109 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1110 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001111 {
1112 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001113 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001114 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001115 }
James Feistb4383f42018-08-06 16:54:10 -07001116
1117 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1118 globalSchema);
1119 if (!schemaStream.good())
1120 {
1121 std::cerr
1122 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1123 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001124 return false;
James Feistb4383f42018-08-06 16:54:10 -07001125 }
1126 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1127 if (schema.is_discarded())
1128 {
1129 std::cerr
1130 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1131 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001132 return false;
James Feistb4383f42018-08-06 16:54:10 -07001133 }
1134
James Feista465ccc2019-02-08 12:51:01 -08001135 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001136 {
1137 std::ifstream jsonStream(jsonPath.c_str());
1138 if (!jsonStream.good())
1139 {
1140 std::cerr << "unable to open " << jsonPath.string() << "\n";
1141 continue;
1142 }
1143 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1144 if (data.is_discarded())
1145 {
1146 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1147 continue;
1148 }
James Feist8da99192019-01-24 08:20:16 -08001149 /*
1150 * todo(james): reenable this once less things are in flight
1151 *
James Feistb4383f42018-08-06 16:54:10 -07001152 if (!validateJson(schema, data))
1153 {
1154 std::cerr << "Error validating " << jsonPath.string() << "\n";
1155 continue;
1156 }
James Feist8da99192019-01-24 08:20:16 -08001157 */
James Feistb4383f42018-08-06 16:54:10 -07001158
James Feist3cb5fec2018-01-23 14:41:51 -08001159 if (data.type() == nlohmann::json::value_t::array)
1160 {
James Feista465ccc2019-02-08 12:51:01 -08001161 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001162 {
1163 configurations.emplace_back(d);
1164 }
1165 }
1166 else
1167 {
1168 configurations.emplace_back(data);
1169 }
1170 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001171 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001172}
James Feist3cb5fec2018-01-23 14:41:51 -08001173
James Feist94219d12020-03-03 11:52:25 -08001174std::string getRecordName(
1175 const boost::container::flat_map<std::string, BasicVariantType>& probe,
1176 const std::string& probeName)
1177{
1178 if (probe.empty())
1179 {
1180 return probeName;
1181 }
1182
1183 // use an array so alphabetical order from the
1184 // flat_map is maintained
1185 auto device = nlohmann::json::array();
1186 for (auto& devPair : probe)
1187 {
1188 device.push_back(devPair.first);
1189 std::visit([&device](auto&& v) { device.push_back(v); },
1190 devPair.second);
1191 }
1192 size_t hash = std::hash<std::string>{}(probeName + device.dump());
1193 // hashes are hard to distinguish, use the
1194 // non-hashed version if we want debug
1195 if constexpr (DEBUG)
1196 {
1197 return probeName + device.dump();
1198 }
1199 else
1200 {
1201 return std::to_string(hash);
1202 }
1203}
1204
James Feist4dc617b2020-05-01 09:54:47 -07001205PerformScan::PerformScan(nlohmann::json& systemConfiguration,
1206 nlohmann::json& missingConfigurations,
1207 std::list<nlohmann::json>& configurations,
1208 sdbusplus::asio::object_server& objServerIn,
1209 std::function<void()>&& callback) :
James Feist733f7652019-11-13 14:32:29 -08001210 _systemConfiguration(systemConfiguration),
1211 _missingConfigurations(missingConfigurations),
James Feist4dc617b2020-05-01 09:54:47 -07001212 _configurations(configurations), objServer(objServerIn),
1213 _callback(std::move(callback))
James Feist8c505da2020-05-28 10:06:33 -07001214{}
James Feist733f7652019-11-13 14:32:29 -08001215void PerformScan::run()
1216{
1217 boost::container::flat_set<std::string> dbusProbeInterfaces;
1218 std::vector<std::shared_ptr<PerformProbe>> dbusProbePointers;
James Feist75fdeeb2018-02-20 14:26:16 -08001219
James Feist733f7652019-11-13 14:32:29 -08001220 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001221 {
James Feist733f7652019-11-13 14:32:29 -08001222 auto findProbe = it->find("Probe");
1223 auto findName = it->find("Name");
James Feist787c3c32019-11-07 14:42:58 -08001224
James Feist733f7652019-11-13 14:32:29 -08001225 nlohmann::json probeCommand;
1226 // check for poorly formatted fields, probe must be an array
1227 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001228 {
James Feist733f7652019-11-13 14:32:29 -08001229 std::cerr << "configuration file missing probe:\n " << *it << "\n";
1230 it = _configurations.erase(it);
1231 continue;
1232 }
1233 else if ((*findProbe).type() != nlohmann::json::value_t::array)
1234 {
1235 probeCommand = nlohmann::json::array();
1236 probeCommand.push_back(*findProbe);
1237 }
1238 else
1239 {
1240 probeCommand = *findProbe;
1241 }
James Feist3cb5fec2018-01-23 14:41:51 -08001242
James Feist733f7652019-11-13 14:32:29 -08001243 if (findName == it->end())
1244 {
1245 std::cerr << "configuration file missing name:\n " << *it << "\n";
1246 it = _configurations.erase(it);
1247 continue;
1248 }
1249 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001250
James Feist733f7652019-11-13 14:32:29 -08001251 if (std::find(passedProbes.begin(), passedProbes.end(), probeName) !=
1252 passedProbes.end())
1253 {
1254 it = _configurations.erase(it);
1255 continue;
1256 }
1257 nlohmann::json* recordPtr = &(*it);
James Feist1b2e2242018-01-30 13:45:19 -08001258
James Feist733f7652019-11-13 14:32:29 -08001259 // store reference to this to children to makes sure we don't get
1260 // destroyed too early
1261 auto thisRef = shared_from_this();
1262 auto probePointer = std::make_shared<PerformProbe>(
1263 probeCommand, thisRef,
1264 [&, recordPtr, probeName](FoundDeviceT& foundDevices) {
James Feist08a5b172019-08-28 14:47:47 -07001265 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001266
James Feist35f5e0e2020-03-16 14:02:27 -07001267 std::set<nlohmann::json> usedNames;
James Feist733f7652019-11-13 14:32:29 -08001268 passedProbes.push_back(probeName);
James Feist94219d12020-03-03 11:52:25 -08001269 std::list<size_t> indexes(foundDevices.size());
1270 std::iota(indexes.begin(), indexes.end(), 1);
James Feist8f2710a2018-05-09 17:18:55 -07001271
James Feist35f5e0e2020-03-16 14:02:27 -07001272 size_t indexIdx = probeName.find("$");
1273 bool hasTemplateName = (indexIdx != std::string::npos);
James Feist94219d12020-03-03 11:52:25 -08001274
1275 // copy over persisted configurations and make sure we remove
1276 // indexes that are already used
1277 for (auto itr = foundDevices.begin();
1278 itr != foundDevices.end();)
James Feist08a5b172019-08-28 14:47:47 -07001279 {
James Feist94219d12020-03-03 11:52:25 -08001280 std::string recordName = getRecordName(*itr, probeName);
James Feistf1b14142019-04-10 15:22:09 -07001281
James Feist08a5b172019-08-28 14:47:47 -07001282 auto fromLastJson = lastJson.find(recordName);
1283 if (fromLastJson != lastJson.end())
1284 {
James Feist02d2b932020-02-06 16:28:48 -08001285 auto findExposes = fromLastJson->find("Exposes");
1286 // delete nulls from any updates
1287 if (findExposes != fromLastJson->end())
1288 {
1289 auto copy = nlohmann::json::array();
1290 for (auto& expose : *findExposes)
1291 {
1292 if (expose.is_null())
1293 {
1294 continue;
1295 }
1296 copy.emplace_back(expose);
1297 }
1298 *findExposes = copy;
1299 }
1300
James Feist08a5b172019-08-28 14:47:47 -07001301 // keep user changes
1302 _systemConfiguration[recordName] = *fromLastJson;
James Feist899e17f2019-09-13 11:46:29 -07001303 _missingConfigurations.erase(recordName);
James Feist94219d12020-03-03 11:52:25 -08001304 itr = foundDevices.erase(itr);
James Feist35f5e0e2020-03-16 14:02:27 -07001305 if (hasTemplateName)
James Feist94219d12020-03-03 11:52:25 -08001306 {
1307 auto nameIt = fromLastJson->find("Name");
1308 if (nameIt == fromLastJson->end())
1309 {
1310 std::cerr << "Last JSON Illegal\n";
1311 continue;
1312 }
1313
1314 int index = std::stoi(
1315 nameIt->get<std::string>().substr(indexIdx),
1316 nullptr, 0);
James Feist35f5e0e2020-03-16 14:02:27 -07001317 usedNames.insert(nameIt.value());
James Feist94219d12020-03-03 11:52:25 -08001318 auto usedIt = std::find(indexes.begin(),
1319 indexes.end(), index);
1320
1321 if (usedIt == indexes.end())
1322 {
1323 continue; // less items now
1324 }
1325 indexes.erase(usedIt);
1326 }
1327
James Feist08a5b172019-08-28 14:47:47 -07001328 continue;
1329 }
James Feist94219d12020-03-03 11:52:25 -08001330 itr++;
1331 }
James Feist35f5e0e2020-03-16 14:02:27 -07001332
1333 std::optional<std::string> replaceStr;
1334
James Feist94219d12020-03-03 11:52:25 -08001335 for (auto& foundDevice : foundDevices)
1336 {
1337 nlohmann::json record = *recordPtr;
1338 std::string recordName =
1339 getRecordName(foundDevice, probeName);
1340 size_t foundDeviceIdx = indexes.front();
1341 indexes.pop_front();
James Feistf1b14142019-04-10 15:22:09 -07001342
James Feist35f5e0e2020-03-16 14:02:27 -07001343 // check name first so we have no duplicate names
1344 auto getName = record.find("Name");
1345 if (getName == record.end())
1346 {
1347 std::cerr << "Record Missing Name! " << record.dump();
1348 continue; // this should be impossible at this level
1349 }
1350
1351 nlohmann::json copyForName = {{"Name", getName.value()}};
1352 nlohmann::json::iterator copyIt = copyForName.begin();
1353 std::optional<std::string> replaceVal = templateCharReplace(
1354 copyIt, foundDevice, foundDeviceIdx, replaceStr);
1355
1356 if (!replaceStr && replaceVal)
1357 {
1358 if (usedNames.find(copyIt.value()) != usedNames.end())
1359 {
1360 replaceStr = replaceVal;
1361 copyForName = {{"Name", getName.value()}};
1362 copyIt = copyForName.begin();
1363 templateCharReplace(copyIt, foundDevice,
1364 foundDeviceIdx, replaceStr);
1365 }
1366 }
1367
1368 if (replaceStr)
1369 {
1370 std::cerr << "Duplicates found, replacing "
1371 << *replaceStr
1372 << " with found device index.\n Consider "
1373 "fixing template to not have duplicates\n";
1374 }
James Feist08a5b172019-08-28 14:47:47 -07001375
1376 for (auto keyPair = record.begin(); keyPair != record.end();
1377 keyPair++)
1378 {
James Feist35f5e0e2020-03-16 14:02:27 -07001379 if (keyPair.key() == "Name")
1380 {
1381 keyPair.value() = copyIt.value();
1382 usedNames.insert(copyIt.value());
1383
1384 continue; // already covered above
1385 }
James Feist08a5b172019-08-28 14:47:47 -07001386 templateCharReplace(keyPair, foundDevice,
James Feist35f5e0e2020-03-16 14:02:27 -07001387 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001388 }
1389
James Feist35f5e0e2020-03-16 14:02:27 -07001390 // insert into configuration temporarily to be able to
1391 // reference ourselves
1392
1393 _systemConfiguration[recordName] = record;
1394
James Feist08a5b172019-08-28 14:47:47 -07001395 auto findExpose = record.find("Exposes");
1396 if (findExpose == record.end())
1397 {
James Feistf1b14142019-04-10 15:22:09 -07001398 _systemConfiguration[recordName] = record;
James Feist08a5b172019-08-28 14:47:47 -07001399 continue;
1400 }
James Feistf1b14142019-04-10 15:22:09 -07001401
James Feist08a5b172019-08-28 14:47:47 -07001402 for (auto& expose : *findExpose)
1403 {
1404 for (auto keyPair = expose.begin();
1405 keyPair != expose.end(); keyPair++)
James Feistf1b14142019-04-10 15:22:09 -07001406 {
James Feist08a5b172019-08-28 14:47:47 -07001407
1408 templateCharReplace(keyPair, foundDevice,
James Feist35f5e0e2020-03-16 14:02:27 -07001409 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001410
James Feist668bbb12020-02-05 14:27:26 -08001411 bool isBind =
1412 boost::starts_with(keyPair.key(), "Bind");
1413 bool isDisable = keyPair.key() == "DisableNode";
1414
1415 // special cases
1416 if (!(isBind || isDisable))
James Feistf1b14142019-04-10 15:22:09 -07001417 {
James Feist668bbb12020-02-05 14:27:26 -08001418 continue;
1419 }
1420
1421 if (keyPair.value().type() !=
1422 nlohmann::json::value_t::string &&
1423 keyPair.value().type() !=
1424 nlohmann::json::value_t::array)
1425 {
1426 std::cerr << "Value is invalid type "
1427 << keyPair.key() << "\n";
1428 continue;
1429 }
1430
1431 std::vector<std::string> matches;
1432 if (keyPair.value().type() ==
1433 nlohmann::json::value_t::string)
1434 {
1435 matches.emplace_back(keyPair.value());
1436 }
1437 else
1438 {
1439 for (const auto& value : keyPair.value())
James Feistf1b14142019-04-10 15:22:09 -07001440 {
James Feist668bbb12020-02-05 14:27:26 -08001441 if (value.type() !=
1442 nlohmann::json::value_t::string)
1443 {
1444 std::cerr << "Value is invalid type "
1445 << value << "\n";
1446 break;
1447 }
1448 matches.emplace_back(value);
James Feistf1b14142019-04-10 15:22:09 -07001449 }
James Feist668bbb12020-02-05 14:27:26 -08001450 }
James Feist08a5b172019-08-28 14:47:47 -07001451
James Feist668bbb12020-02-05 14:27:26 -08001452 std::set<std::string> foundMatches;
1453 for (auto& configurationPair :
1454 _systemConfiguration.items())
1455 {
1456 if (isDisable)
James Feist8f2710a2018-05-09 17:18:55 -07001457 {
James Feist668bbb12020-02-05 14:27:26 -08001458 // don't disable ourselves
1459 if (configurationPair.key() == recordName)
James Feist3cb5fec2018-01-23 14:41:51 -08001460 {
James Feist1b2e2242018-01-30 13:45:19 -08001461 continue;
1462 }
James Feist668bbb12020-02-05 14:27:26 -08001463 }
1464 auto configListFind =
1465 configurationPair.value().find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001466
James Feist668bbb12020-02-05 14:27:26 -08001467 if (configListFind ==
1468 configurationPair.value().end() ||
1469 configListFind->type() !=
1470 nlohmann::json::value_t::array)
James Feist08a5b172019-08-28 14:47:47 -07001471 {
James Feist668bbb12020-02-05 14:27:26 -08001472 continue;
James Feist08a5b172019-08-28 14:47:47 -07001473 }
James Feist668bbb12020-02-05 14:27:26 -08001474 for (auto& exposedObject : *configListFind)
1475 {
1476 auto matchIt = std::find_if(
1477 matches.begin(), matches.end(),
1478 [name = (exposedObject)["Name"]
1479 .get<std::string>()](
1480 const std::string& s) {
1481 return s == name;
1482 });
1483 if (matchIt == matches.end())
1484 {
1485 continue;
1486 }
1487 foundMatches.insert(*matchIt);
1488
1489 if (isBind)
1490 {
1491 std::string bind = keyPair.key().substr(
1492 sizeof("Bind") - 1);
1493
1494 exposedObject["Status"] = "okay";
1495 expose[bind] = exposedObject;
1496 }
1497 else if (isDisable)
1498 {
1499 exposedObject["Status"] = "disabled";
1500 }
1501 }
1502 }
1503 if (foundMatches.size() != matches.size())
1504 {
1505 std::cerr << "configuration file "
1506 "dependency error, "
1507 "could not find "
1508 << keyPair.key() << " "
1509 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001510 }
1511 }
1512 }
James Feist08a5b172019-08-28 14:47:47 -07001513 // overwrite ourselves with cleaned up version
1514 _systemConfiguration[recordName] = record;
James Feist899e17f2019-09-13 11:46:29 -07001515 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001516 }
1517 });
James Feist787c3c32019-11-07 14:42:58 -08001518
James Feist733f7652019-11-13 14:32:29 -08001519 // parse out dbus probes by discarding other probe types, store in a
1520 // map
1521 for (const std::string& probe : probeCommand)
1522 {
1523 bool found = false;
1524 boost::container::flat_map<const char*, probe_type_codes,
1525 cmp_str>::const_iterator probeType;
1526 for (probeType = PROBE_TYPES.begin();
1527 probeType != PROBE_TYPES.end(); ++probeType)
James Feist787c3c32019-11-07 14:42:58 -08001528 {
James Feist733f7652019-11-13 14:32:29 -08001529 if (probe.find(probeType->first) != std::string::npos)
James Feist787c3c32019-11-07 14:42:58 -08001530 {
James Feist733f7652019-11-13 14:32:29 -08001531 found = true;
1532 break;
James Feist787c3c32019-11-07 14:42:58 -08001533 }
James Feist787c3c32019-11-07 14:42:58 -08001534 }
James Feist733f7652019-11-13 14:32:29 -08001535 if (found)
1536 {
1537 continue;
1538 }
1539 // syntax requires probe before first open brace
1540 auto findStart = probe.find("(");
1541 std::string interface = probe.substr(0, findStart);
1542 dbusProbeInterfaces.emplace(interface);
1543 dbusProbePointers.emplace_back(probePointer);
James Feist3cb5fec2018-01-23 14:41:51 -08001544 }
James Feist733f7652019-11-13 14:32:29 -08001545 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001546 }
James Feist75fdeeb2018-02-20 14:26:16 -08001547
James Feist4dc617b2020-05-01 09:54:47 -07001548 for (const std::string& interface : dbusProbeInterfaces)
1549 {
1550 registerCallback(_systemConfiguration, objServer, interface);
1551 }
1552
James Feist733f7652019-11-13 14:32:29 -08001553 // probe vector stores a shared_ptr to each PerformProbe that cares
1554 // about a dbus interface
James Feistb1728ca2020-04-30 15:40:55 -07001555 findDbusObjects(std::move(dbusProbePointers),
1556 std::move(dbusProbeInterfaces), shared_from_this());
1557 if constexpr (DEBUG)
1558 {
1559 std::cerr << __func__ << " " << __LINE__ << "\n";
1560 }
James Feist733f7652019-11-13 14:32:29 -08001561}
1562
1563PerformScan::~PerformScan()
1564{
1565 if (_passed)
James Feist75fdeeb2018-02-20 14:26:16 -08001566 {
James Feist733f7652019-11-13 14:32:29 -08001567 auto nextScan = std::make_shared<PerformScan>(
1568 _systemConfiguration, _missingConfigurations, _configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001569 objServer, std::move(_callback));
James Feist733f7652019-11-13 14:32:29 -08001570 nextScan->passedProbes = std::move(passedProbes);
1571 nextScan->dbusProbeObjects = std::move(dbusProbeObjects);
1572 nextScan->run();
James Feistb1728ca2020-04-30 15:40:55 -07001573
1574 if constexpr (DEBUG)
1575 {
1576 std::cerr << __func__ << " " << __LINE__ << "\n";
1577 }
James Feist8f2710a2018-05-09 17:18:55 -07001578 }
James Feist733f7652019-11-13 14:32:29 -08001579 else
1580 {
James Feist4dc617b2020-05-01 09:54:47 -07001581 _callback();
James Feistb1728ca2020-04-30 15:40:55 -07001582
1583 if constexpr (DEBUG)
1584 {
1585 std::cerr << __func__ << " " << __LINE__ << "\n";
1586 }
James Feist733f7652019-11-13 14:32:29 -08001587 }
1588}
James Feistc95cb142018-02-26 10:41:42 -08001589
James Feistb1728ca2020-04-30 15:40:55 -07001590void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001591 nlohmann::json& systemConfiguration)
1592{
1593 static bool scannedPowerOff = false;
1594 static bool scannedPowerOn = false;
1595
James Feistfb00f392019-06-25 14:16:48 -07001596 if (systemConfiguration.empty() || lastJson.empty())
1597 {
1598 return; // not ready yet
1599 }
James Feist1df06a42019-04-11 14:23:04 -07001600 if (scannedPowerOn)
1601 {
1602 return;
1603 }
1604
1605 if (!isPowerOn() && scannedPowerOff)
1606 {
1607 return;
1608 }
1609
James Feistb1728ca2020-04-30 15:40:55 -07001610 timer.expires_after(std::chrono::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001611 timer.async_wait(
1612 [&systemConfiguration](const boost::system::error_code& ec) {
1613 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001614 {
James Feist1a996582019-05-14 15:10:06 -07001615 // we were cancelled
1616 return;
1617 }
1618
1619 bool powerOff = !isPowerOn();
1620 for (const auto& item : lastJson.items())
1621 {
1622 if (systemConfiguration.find(item.key()) ==
1623 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001624 {
James Feist1a996582019-05-14 15:10:06 -07001625 bool isDetectedPowerOn = false;
1626 auto powerState = item.value().find("PowerState");
1627 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001628 {
James Feist1a996582019-05-14 15:10:06 -07001629 auto ptr = powerState->get_ptr<const std::string*>();
1630 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001631 {
James Feist1a996582019-05-14 15:10:06 -07001632 if (*ptr == "On" || *ptr == "BiosPost")
1633 {
1634 isDetectedPowerOn = true;
1635 }
James Feist1df06a42019-04-11 14:23:04 -07001636 }
1637 }
James Feist1a996582019-05-14 15:10:06 -07001638 if (powerOff && isDetectedPowerOn)
1639 {
1640 // power not on yet, don't know if it's there or not
1641 continue;
1642 }
1643 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1644 {
1645 // already logged it when power was off
1646 continue;
1647 }
James Feist1df06a42019-04-11 14:23:04 -07001648
James Feist1a996582019-05-14 15:10:06 -07001649 logDeviceRemoved(item.value());
1650 }
James Feist1df06a42019-04-11 14:23:04 -07001651 }
James Feist1a996582019-05-14 15:10:06 -07001652 scannedPowerOff = true;
1653 if (!powerOff)
1654 {
1655 scannedPowerOn = true;
1656 }
1657 });
James Feist1df06a42019-04-11 14:23:04 -07001658}
1659
James Feist8f2710a2018-05-09 17:18:55 -07001660// main properties changed entry
James Feist4dc617b2020-05-01 09:54:47 -07001661void propertiesChangedCallback(nlohmann::json& systemConfiguration,
1662 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001663{
James Feist2539ccd2020-05-01 16:15:08 -07001664 static bool inProgress = false;
James Feistb1728ca2020-04-30 15:40:55 -07001665 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001666 static size_t instance = 0;
1667 instance++;
1668 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001669
James Feistb1728ca2020-04-30 15:40:55 -07001670 timer.expires_after(std::chrono::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001671
1672 // setup an async wait as we normally get flooded with new requests
James Feist4dc617b2020-05-01 09:54:47 -07001673 timer.async_wait([&systemConfiguration, &objServer,
James Feist899e17f2019-09-13 11:46:29 -07001674 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001675 if (ec == boost::asio::error::operation_aborted)
1676 {
1677 // we were cancelled
1678 return;
1679 }
1680 else if (ec)
1681 {
1682 std::cerr << "async wait error " << ec << "\n";
1683 return;
1684 }
1685
James Feist2539ccd2020-05-01 16:15:08 -07001686 if (inProgress)
1687 {
1688 propertiesChangedCallback(systemConfiguration, objServer);
1689 return;
1690 }
1691 inProgress = true;
1692
James Feist8f2710a2018-05-09 17:18:55 -07001693 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001694 auto missingConfigurations = std::make_shared<nlohmann::json>();
1695 *missingConfigurations = systemConfiguration;
1696
James Feist8f2710a2018-05-09 17:18:55 -07001697 std::list<nlohmann::json> configurations;
1698 if (!findJsonFiles(configurations))
1699 {
1700 std::cerr << "cannot find json files\n";
James Feist2539ccd2020-05-01 16:15:08 -07001701 inProgress = false;
James Feist8f2710a2018-05-09 17:18:55 -07001702 return;
1703 }
1704
1705 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001706 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001707 objServer,
1708 [&systemConfiguration, &objServer, count, oldConfiguration,
1709 missingConfigurations]() {
James Feist899e17f2019-09-13 11:46:29 -07001710 // this is something that since ac has been applied to the bmc
1711 // we saw, and we no longer see it
1712 bool powerOff = !isPowerOn();
1713 for (const auto& item : missingConfigurations->items())
1714 {
1715 bool isDetectedPowerOn = false;
1716 auto powerState = item.value().find("PowerState");
1717 if (powerState != item.value().end())
1718 {
1719 auto ptr = powerState->get_ptr<const std::string*>();
1720 if (ptr)
1721 {
1722 if (*ptr == "On" || *ptr == "BiosPost")
1723 {
1724 isDetectedPowerOn = true;
1725 }
1726 }
1727 }
1728 if (powerOff && isDetectedPowerOn)
1729 {
1730 // power not on yet, don't know if it's there or not
1731 continue;
1732 }
1733 std::string name = item.value()["Name"].get<std::string>();
James Feist02d2b932020-02-06 16:28:48 -08001734 std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
James Feist899e17f2019-09-13 11:46:29 -07001735 ifaces = inventory[name];
1736 for (auto& iface : ifaces)
1737 {
James Feist02d2b932020-02-06 16:28:48 -08001738 auto sharedPtr = iface.lock();
1739 if (!sharedPtr)
1740 {
1741 continue; // was already deleted elsewhere
1742 }
1743 objServer.remove_interface(sharedPtr);
James Feist899e17f2019-09-13 11:46:29 -07001744 }
1745 ifaces.clear();
1746 systemConfiguration.erase(item.key());
1747 logDeviceRemoved(item.value());
1748 }
1749
James Feist8f2710a2018-05-09 17:18:55 -07001750 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001751 for (auto it = newConfiguration.begin();
1752 it != newConfiguration.end();)
1753 {
1754 auto findKey = oldConfiguration.find(it.key());
1755 if (findKey != oldConfiguration.end())
1756 {
1757 it = newConfiguration.erase(it);
1758 }
1759 else
1760 {
1761 it++;
1762 }
1763 }
James Feist899e17f2019-09-13 11:46:29 -07001764 for (const auto& item : newConfiguration.items())
1765 {
1766 logDeviceAdded(item.value());
1767 }
1768
James Feist2539ccd2020-05-01 16:15:08 -07001769 inProgress = false;
1770
James Feist8f2710a2018-05-09 17:18:55 -07001771 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001772 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001773
James Feistbb43d022018-06-12 15:44:33 -07001774 io.post([&]() {
1775 if (!writeJsonFiles(systemConfiguration))
1776 {
1777 std::cerr << "Error writing json files\n";
1778 }
1779 });
James Feist8f2710a2018-05-09 17:18:55 -07001780 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001781 postToDbus(newConfiguration, systemConfiguration,
1782 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001783 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001784 {
James Feist899e17f2019-09-13 11:46:29 -07001785 return;
James Feist1df06a42019-04-11 14:23:04 -07001786 }
James Feist899e17f2019-09-13 11:46:29 -07001787 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001788 });
1789 });
1790 });
1791 perfScan->run();
1792 });
James Feist75fdeeb2018-02-20 14:26:16 -08001793}
1794
James Feist4dc617b2020-05-01 09:54:47 -07001795void registerCallback(nlohmann::json& systemConfiguration,
1796 sdbusplus::asio::object_server& objServer,
1797 const std::string& interface)
James Feist75fdeeb2018-02-20 14:26:16 -08001798{
James Feist4dc617b2020-05-01 09:54:47 -07001799 static boost::container::flat_map<std::string, sdbusplus::bus::match::match>
1800 dbusMatches;
James Feist75fdeeb2018-02-20 14:26:16 -08001801
James Feist4dc617b2020-05-01 09:54:47 -07001802 auto find = dbusMatches.find(interface);
1803 if (find != dbusMatches.end())
James Feist75fdeeb2018-02-20 14:26:16 -08001804 {
James Feist4dc617b2020-05-01 09:54:47 -07001805 return;
James Feist75fdeeb2018-02-20 14:26:16 -08001806 }
James Feist4dc617b2020-05-01 09:54:47 -07001807 std::function<void(sdbusplus::message::message & message)> eventHandler =
1808
1809 [&](sdbusplus::message::message&) {
1810 propertiesChangedCallback(systemConfiguration, objServer);
1811 };
1812
1813 sdbusplus::bus::match::match match(
1814 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
1815 "type='signal',member='PropertiesChanged',arg0='" + interface + "'",
1816 eventHandler);
1817 dbusMatches.emplace(interface, std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001818}
1819
James Feist98132792019-07-09 13:29:09 -07001820int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001821{
1822 // setup connection to dbus
James Feist8f2710a2018-05-09 17:18:55 -07001823 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001824 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001825
James Feist8f2710a2018-05-09 17:18:55 -07001826 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001827
James Feist8f2710a2018-05-09 17:18:55 -07001828 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1829 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1830 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001831
James Feist4131aea2018-03-09 09:47:30 -08001832 // to keep reference to the match / filter objects so they don't get
1833 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001834
1835 nlohmann::json systemConfiguration = nlohmann::json::object();
1836
James Feist4dc617b2020-05-01 09:54:47 -07001837 io.post(
1838 [&]() { propertiesChangedCallback(systemConfiguration, objServer); });
James Feist4131aea2018-03-09 09:47:30 -08001839
James Feistfd1264a2018-05-03 12:10:00 -07001840 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -07001841 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001842 });
James Feist8f2710a2018-05-09 17:18:55 -07001843 entityIface->initialize();
1844
James Feist1df06a42019-04-11 14:23:04 -07001845 if (fwVersionIsSame())
1846 {
1847 if (std::filesystem::is_regular_file(currentConfiguration))
1848 {
1849 // this file could just be deleted, but it's nice for debug
1850 std::filesystem::create_directory(tempConfigDir);
1851 std::filesystem::remove(lastConfiguration);
1852 std::filesystem::copy(currentConfiguration, lastConfiguration);
1853 std::filesystem::remove(currentConfiguration);
1854
1855 std::ifstream jsonStream(lastConfiguration);
1856 if (jsonStream.good())
1857 {
1858 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1859 if (data.is_discarded())
1860 {
1861 std::cerr << "syntax error in " << lastConfiguration
1862 << "\n";
1863 }
1864 else
1865 {
1866 lastJson = std::move(data);
1867 }
1868 }
1869 else
1870 {
1871 std::cerr << "unable to open " << lastConfiguration << "\n";
1872 }
1873 }
1874 }
1875 else
1876 {
1877 // not an error, just logging at this level to make it in the journal
1878 std::cerr << "Clearing previous configuration\n";
1879 std::filesystem::remove(currentConfiguration);
1880 }
1881
1882 // some boards only show up after power is on, we want to not say they are
1883 // removed until the same state happens
1884 setupPowerMatch(SYSTEM_BUS);
1885
James Feist1b2e2242018-01-30 13:45:19 -08001886 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001887
1888 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001889}