blob: 2ab88e3391886ccbaf09319eb32480c66236308e [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 Bishope45d8c72022-05-25 15:12:53 -040016/// \file entity_manager.cpp
James Feist3cb5fec2018-01-23 14:41:51 -080017
Brad Bishope45d8c72022-05-25 15:12:53 -040018#include "entity_manager.hpp"
James Feist1df06a42019-04-11 14:23:04 -070019
Brad Bishope45d8c72022-05-25 15:12:53 -040020#include "overlay.hpp"
21#include "utils.hpp"
22#include "variant_visitors.hpp"
James Feist481c5d52019-08-13 14:40:40 -070023
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
Igor Kononenko9fd87e52020-10-06 01:18:17 +030038#include <charconv>
James Feist637b3ef2019-04-15 16:35:30 -070039#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080040#include <fstream>
Andrew Jefferye35d0ac2022-03-24 15:53:13 +103041#include <functional>
James Feista465ccc2019-02-08 12:51:01 -080042#include <iostream>
Andrew Jeffery666583b2021-12-01 15:50:12 +103043#include <map>
James Feista465ccc2019-02-08 12:51:01 -080044#include <regex>
James Feista465ccc2019-02-08 12:51:01 -080045#include <variant>
Andrew Jefferya9c58922021-06-01 09:28:59 +093046constexpr const char* hostConfigurationDirectory = SYSCONF_DIR "configurations";
James Feista465ccc2019-02-08 12:51:01 -080047constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
48constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070049constexpr const char* tempConfigDir = "/tmp/configuration/";
50constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
51constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080052constexpr const char* globalSchema = "global.json";
James Feistf1b14142019-04-10 15:22:09 -070053
Andrew Jeffery666583b2021-12-01 15:50:12 +103054const boost::container::flat_map<const char*, probe_type_codes, CmpStr>
Ed Tanous07d467b2021-02-23 14:48:37 -080055 probeTypes{{{"FALSE", probe_type_codes::FALSE_T},
56 {"TRUE", probe_type_codes::TRUE_T},
57 {"AND", probe_type_codes::AND},
58 {"OR", probe_type_codes::OR},
59 {"FOUND", probe_type_codes::FOUND},
60 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080061
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +020062static constexpr std::array<const char*, 6> settableInterfaces = {
63 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
James Feist68500ff2018-08-08 15:40:42 -070064using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080065 std::variant<std::vector<std::string>, std::vector<double>, std::string,
66 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
67 uint16_t, uint8_t, bool>;
James Feist3cb5fec2018-01-23 14:41:51 -080068
James Feistd58879a2019-09-11 11:26:07 -070069// store reference to all interfaces so we can destroy them later
70boost::container::flat_map<
James Feist02d2b932020-02-06 16:28:48 -080071 std::string, std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>>
James Feistd58879a2019-09-11 11:26:07 -070072 inventory;
73
James Feist3cb5fec2018-01-23 14:41:51 -080074// todo: pass this through nicer
Ed Tanous07d467b2021-02-23 14:48:37 -080075std::shared_ptr<sdbusplus::asio::connection> systemBus;
Andrew Jeffery47af65a2021-12-01 14:16:31 +103076nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -080077
James Feist02d2b932020-02-06 16:28:48 -080078boost::asio::io_context io;
79
Ed Tanous07d467b2021-02-23 14:48:37 -080080const std::regex illegalDbusPathRegex("[^A-Za-z0-9_.]");
81const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -080082
Andrew Jeffery666583b2021-12-01 15:50:12 +103083FoundProbeTypeT findProbeType(const std::string& probe)
84{
85 boost::container::flat_map<const char*, probe_type_codes,
86 CmpStr>::const_iterator probeType;
87 for (probeType = probeTypes.begin(); probeType != probeTypes.end();
88 ++probeType)
89 {
90 if (probe.find(probeType->first) != std::string::npos)
91 {
92 return probeType;
93 }
94 }
95
96 return std::nullopt;
97}
98
James Feistd58879a2019-09-11 11:26:07 -070099static std::shared_ptr<sdbusplus::asio::dbus_interface>
100 createInterface(sdbusplus::asio::object_server& objServer,
101 const std::string& path, const std::string& interface,
James Feist02d2b932020-02-06 16:28:48 -0800102 const std::string& parent, bool checkNull = false)
James Feistd58879a2019-09-11 11:26:07 -0700103{
James Feist02d2b932020-02-06 16:28:48 -0800104 // on first add we have no reason to check for null before add, as there
105 // won't be any. For dynamically added interfaces, we check for null so that
106 // a constant delete/add will not create a memory leak
107
108 auto ptr = objServer.add_interface(path, interface);
109 auto& dataVector = inventory[parent];
110 if (checkNull)
111 {
112 auto it = std::find_if(dataVector.begin(), dataVector.end(),
113 [](const auto& p) { return p.expired(); });
114 if (it != dataVector.end())
115 {
116 *it = ptr;
117 return ptr;
118 }
119 }
120 dataVector.emplace_back(ptr);
121 return ptr;
James Feistd58879a2019-09-11 11:26:07 -0700122}
123
James Feist8f2710a2018-05-09 17:18:55 -0700124// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800125bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800126{
James Feist1df06a42019-04-11 14:23:04 -0700127 std::filesystem::create_directory(configurationOutDir);
128 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700129 if (!output.good())
130 {
131 return false;
132 }
James Feist1b2e2242018-01-30 13:45:19 -0800133 output << systemConfiguration.dump(4);
134 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700135 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700136}
James Feist1b2e2242018-01-30 13:45:19 -0800137
James Feist97a63f12018-05-17 13:50:57 -0700138template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800139bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
140 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700141{
142 try
143 {
144 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800145 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700146 ref = value;
147 return true;
148 }
James Feist98132792019-07-09 13:29:09 -0700149 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700150 {
151 return false;
152 }
153}
James Feistbb43d022018-06-12 15:44:33 -0700154
James Feistebcc26b2019-03-22 12:30:43 -0700155// template function to add array as dbus property
156template <typename PropertyType>
157void addArrayToDbus(const std::string& name, const nlohmann::json& array,
158 sdbusplus::asio::dbus_interface* iface,
159 sdbusplus::asio::PropertyPermission permission,
160 nlohmann::json& systemConfiguration,
161 const std::string& jsonPointerString)
162{
163 std::vector<PropertyType> values;
164 for (const auto& property : array)
165 {
166 auto ptr = property.get_ptr<const PropertyType*>();
167 if (ptr != nullptr)
168 {
169 values.emplace_back(*ptr);
170 }
171 }
172
173 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
174 {
175 iface->register_property(name, values);
176 }
177 else
178 {
179 iface->register_property(
180 name, values,
181 [&systemConfiguration,
182 jsonPointerString{std::string(jsonPointerString)}](
183 const std::vector<PropertyType>& newVal,
184 std::vector<PropertyType>& val) {
185 val = newVal;
186 if (!setJsonFromPointer(jsonPointerString, val,
187 systemConfiguration))
188 {
189 std::cerr << "error setting json field\n";
190 return -1;
191 }
192 if (!writeJsonFiles(systemConfiguration))
193 {
194 std::cerr << "error setting json file\n";
195 return -1;
196 }
197 return 1;
198 });
199 }
200}
201
James Feistbb43d022018-06-12 15:44:33 -0700202template <typename PropertyType>
Ed Tanous3013fb42022-07-09 08:27:06 -0700203void addProperty(const std::string& name, const PropertyType& value,
James Feista465ccc2019-02-08 12:51:01 -0800204 sdbusplus::asio::dbus_interface* iface,
205 nlohmann::json& systemConfiguration,
206 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700207 sdbusplus::asio::PropertyPermission permission)
208{
209 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
210 {
Ed Tanous3013fb42022-07-09 08:27:06 -0700211 iface->register_property(name, value);
James Feistbb43d022018-06-12 15:44:33 -0700212 return;
213 }
James Feist68500ff2018-08-08 15:40:42 -0700214 iface->register_property(
Ed Tanous3013fb42022-07-09 08:27:06 -0700215 name, value,
James Feist68500ff2018-08-08 15:40:42 -0700216 [&systemConfiguration,
217 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800218 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700219 val = newVal;
220 if (!setJsonFromPointer(jsonPointerString, val,
221 systemConfiguration))
222 {
223 std::cerr << "error setting json field\n";
224 return -1;
225 }
James Feistc6248a52018-08-14 10:09:45 -0700226 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700227 {
228 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700229 return -1;
230 }
231 return 1;
232 });
233}
234
235void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800236 const std::string& jsonPointerPath,
237 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
238 sdbusplus::asio::object_server& objServer,
239 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700240{
241 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
242 iface->register_method(
243 "Delete", [&objServer, &systemConfiguration, interface,
244 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700245 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700246 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700247 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700248 {
249 // this technically can't happen as the pointer is pointing to
250 // us
251 throw DBusInternalError();
252 }
253 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feistc6248a52018-08-14 10:09:45 -0700254 systemConfiguration[ptr] = nullptr;
255
James Feist02d2b932020-02-06 16:28:48 -0800256 // todo(james): dig through sdbusplus to find out why we can't
257 // delete it in a method call
258 io.post([&objServer, dbusInterface]() mutable {
259 objServer.remove_interface(dbusInterface);
260 });
261
James Feistc6248a52018-08-14 10:09:45 -0700262 if (!writeJsonFiles(systemConfiguration))
263 {
264 std::cerr << "error setting json file\n";
265 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700266 }
James Feist68500ff2018-08-08 15:40:42 -0700267 });
James Feistbb43d022018-06-12 15:44:33 -0700268}
269
James Feist1b2e2242018-01-30 13:45:19 -0800270// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700271void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800272 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
273 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
274 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700275 sdbusplus::asio::PropertyPermission permission =
276 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800277{
Patrick Williams2594d362022-09-28 06:46:24 -0500278 for (const auto& [key, value] : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800279 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030280 auto type = value.type();
James Feist8f2710a2018-05-09 17:18:55 -0700281 bool array = false;
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030282 if (value.type() == nlohmann::json::value_t::array)
James Feist8f2710a2018-05-09 17:18:55 -0700283 {
284 array = true;
Ed Tanous3013fb42022-07-09 08:27:06 -0700285 if (value.empty())
James Feist8f2710a2018-05-09 17:18:55 -0700286 {
287 continue;
288 }
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030289 type = value[0].type();
James Feist8f2710a2018-05-09 17:18:55 -0700290 bool isLegal = true;
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030291 for (const auto& arrayItem : value)
James Feist8f2710a2018-05-09 17:18:55 -0700292 {
293 if (arrayItem.type() != type)
294 {
295 isLegal = false;
296 break;
297 }
298 }
299 if (!isLegal)
300 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030301 std::cerr << "dbus format error" << value << "\n";
James Feist8f2710a2018-05-09 17:18:55 -0700302 continue;
303 }
James Feista218ddb2019-04-11 14:01:31 -0700304 }
305 if (type == nlohmann::json::value_t::object)
306 {
307 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700308 }
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030309
310 std::string path = jsonPointerPath;
311 path.append("/").append(key);
James Feistbb43d022018-06-12 15:44:33 -0700312 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
313 {
314 // all setable numbers are doubles as it is difficult to always
315 // create a configuration file with all whole numbers as decimals
316 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700317 if (array)
318 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030319 if (value[0].is_number())
James Feistebcc26b2019-03-22 12:30:43 -0700320 {
321 type = nlohmann::json::value_t::number_float;
322 }
323 }
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030324 else if (value.is_number())
James Feistbb43d022018-06-12 15:44:33 -0700325 {
326 type = nlohmann::json::value_t::number_float;
327 }
328 }
329
James Feist8f2710a2018-05-09 17:18:55 -0700330 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800331 {
James Feist9eb0b582018-04-27 12:15:46 -0700332 case (nlohmann::json::value_t::boolean):
333 {
James Feist8f2710a2018-05-09 17:18:55 -0700334 if (array)
335 {
336 // todo: array of bool isn't detected correctly by
337 // sdbusplus, change it to numbers
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030338 addArrayToDbus<uint64_t>(key, value, iface.get(),
339 permission, systemConfiguration,
340 path);
James Feist8f2710a2018-05-09 17:18:55 -0700341 }
James Feistbb43d022018-06-12 15:44:33 -0700342
James Feist97a63f12018-05-17 13:50:57 -0700343 else
344 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030345 addProperty(key, value.get<bool>(), iface.get(),
346 systemConfiguration, path, permission);
James Feist97a63f12018-05-17 13:50:57 -0700347 }
James Feist9eb0b582018-04-27 12:15:46 -0700348 break;
349 }
350 case (nlohmann::json::value_t::number_integer):
351 {
James Feist8f2710a2018-05-09 17:18:55 -0700352 if (array)
353 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030354 addArrayToDbus<int64_t>(key, value, iface.get(), permission,
Andrew Jeffery029ee282022-03-25 13:11:36 +1030355 systemConfiguration, path);
James Feist97a63f12018-05-17 13:50:57 -0700356 }
357 else
358 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030359 addProperty(key, value.get<int64_t>(), iface.get(),
360 systemConfiguration, path,
James Feistbb43d022018-06-12 15:44:33 -0700361 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700362 }
James Feist9eb0b582018-04-27 12:15:46 -0700363 break;
364 }
365 case (nlohmann::json::value_t::number_unsigned):
366 {
James Feist8f2710a2018-05-09 17:18:55 -0700367 if (array)
368 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030369 addArrayToDbus<uint64_t>(key, value, iface.get(),
370 permission, systemConfiguration,
371 path);
James Feist97a63f12018-05-17 13:50:57 -0700372 }
373 else
374 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030375 addProperty(key, value.get<uint64_t>(), iface.get(),
Andrew Jeffery029ee282022-03-25 13:11:36 +1030376 systemConfiguration, path,
James Feistbb43d022018-06-12 15:44:33 -0700377 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700378 }
James Feist9eb0b582018-04-27 12:15:46 -0700379 break;
380 }
381 case (nlohmann::json::value_t::number_float):
382 {
James Feist8f2710a2018-05-09 17:18:55 -0700383 if (array)
384 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030385 addArrayToDbus<double>(key, value, iface.get(), permission,
Andrew Jeffery029ee282022-03-25 13:11:36 +1030386 systemConfiguration, path);
James Feist8f2710a2018-05-09 17:18:55 -0700387 }
James Feistbb43d022018-06-12 15:44:33 -0700388
James Feist97a63f12018-05-17 13:50:57 -0700389 else
390 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030391 addProperty(key, value.get<double>(), iface.get(),
392 systemConfiguration, path, permission);
James Feist97a63f12018-05-17 13:50:57 -0700393 }
James Feist9eb0b582018-04-27 12:15:46 -0700394 break;
395 }
396 case (nlohmann::json::value_t::string):
397 {
James Feist8f2710a2018-05-09 17:18:55 -0700398 if (array)
399 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030400 addArrayToDbus<std::string>(key, value, iface.get(),
401 permission, systemConfiguration,
402 path);
James Feist97a63f12018-05-17 13:50:57 -0700403 }
404 else
405 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030406 addProperty(key, value.get<std::string>(), iface.get(),
407 systemConfiguration, path, permission);
James Feist97a63f12018-05-17 13:50:57 -0700408 }
James Feist9eb0b582018-04-27 12:15:46 -0700409 break;
410 }
James Feist0eb40352019-04-09 14:44:04 -0700411 default:
412 {
James Feista218ddb2019-04-11 14:01:31 -0700413 std::cerr << "Unexpected json type in system configuration "
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030414 << key << ": " << value.type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700415 break;
416 }
James Feist1b2e2242018-01-30 13:45:19 -0800417 }
418 }
James Feistc6248a52018-08-14 10:09:45 -0700419 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
420 {
421 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
422 systemConfiguration);
423 }
James Feist8f2710a2018-05-09 17:18:55 -0700424 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800425}
426
James Feista465ccc2019-02-08 12:51:01 -0800427sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700428{
429 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
430 interface) != settableInterfaces.end()
431 ? sdbusplus::asio::PropertyPermission::readWrite
432 : sdbusplus::asio::PropertyPermission::readOnly;
433}
434
James Feista465ccc2019-02-08 12:51:01 -0800435void createAddObjectMethod(const std::string& jsonPointerPath,
436 const std::string& path,
437 nlohmann::json& systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700438 sdbusplus::asio::object_server& objServer,
439 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700440{
James Feistd58879a2019-09-11 11:26:07 -0700441 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
442 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700443
444 iface->register_method(
445 "AddObject",
446 [&systemConfiguration, &objServer,
James Feistd58879a2019-09-11 11:26:07 -0700447 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
448 board](const boost::container::flat_map<std::string, JsonVariantType>&
449 data) {
James Feist68500ff2018-08-08 15:40:42 -0700450 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800451 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700452 auto findExposes = base.find("Exposes");
453
454 if (findExposes == base.end())
455 {
456 throw std::invalid_argument("Entity must have children.");
457 }
458
459 // this will throw invalid-argument to sdbusplus if invalid json
460 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800461 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700462 {
James Feista465ccc2019-02-08 12:51:01 -0800463 nlohmann::json& newJson = newData[item.first];
Ed Tanous3013fb42022-07-09 08:27:06 -0700464 std::visit(
465 [&newJson](auto&& val) {
466 newJson = std::forward<decltype(val)>(val);
467 },
468 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700469 }
470
471 auto findName = newData.find("Name");
472 auto findType = newData.find("Type");
473 if (findName == newData.end() || findType == newData.end())
474 {
475 throw std::invalid_argument("AddObject missing Name or Type");
476 }
James Feista465ccc2019-02-08 12:51:01 -0800477 const std::string* type = findType->get_ptr<const std::string*>();
478 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700479 if (type == nullptr || name == nullptr)
480 {
481 throw std::invalid_argument("Type and Name must be a string.");
482 }
483
James Feist02d2b932020-02-06 16:28:48 -0800484 bool foundNull = false;
James Feist68500ff2018-08-08 15:40:42 -0700485 size_t lastIndex = 0;
486 // we add in the "exposes"
James Feist02d2b932020-02-06 16:28:48 -0800487 for (const auto& expose : *findExposes)
James Feist68500ff2018-08-08 15:40:42 -0700488 {
James Feist02d2b932020-02-06 16:28:48 -0800489 if (expose.is_null())
490 {
491 foundNull = true;
492 continue;
493 }
494
495 if (expose["Name"] == *name && expose["Type"] == *type)
James Feist68500ff2018-08-08 15:40:42 -0700496 {
497 throw std::invalid_argument(
498 "Field already in JSON, not adding");
499 }
James Feist02d2b932020-02-06 16:28:48 -0800500
501 if (foundNull)
502 {
503 continue;
504 }
505
James Feist68500ff2018-08-08 15:40:42 -0700506 lastIndex++;
507 }
508
509 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
Potin Laib068d432022-11-02 18:04:21 +0800510 boost::to_lower_copy(*type) + ".json");
James Feist68500ff2018-08-08 15:40:42 -0700511 // todo(james) we might want to also make a list of 'can add'
512 // interfaces but for now I think the assumption if there is a
513 // schema avaliable that it is allowed to update is fine
514 if (!schemaFile.good())
515 {
516 throw std::invalid_argument(
517 "No schema avaliable, cannot validate.");
518 }
519 nlohmann::json schema =
520 nlohmann::json::parse(schemaFile, nullptr, false);
521 if (schema.is_discarded())
522 {
523 std::cerr << "Schema not legal" << *type << ".json\n";
524 throw DBusInternalError();
525 }
526 if (!validateJson(schema, newData))
527 {
528 throw std::invalid_argument("Data does not match schema");
529 }
James Feist02d2b932020-02-06 16:28:48 -0800530 if (foundNull)
531 {
532 findExposes->at(lastIndex) = newData;
533 }
534 else
535 {
536 findExposes->push_back(newData);
537 }
James Feist68500ff2018-08-08 15:40:42 -0700538 if (!writeJsonFiles(systemConfiguration))
539 {
540 std::cerr << "Error writing json files\n";
541 throw DBusInternalError();
542 }
543 std::string dbusName = *name;
544
545 std::regex_replace(dbusName.begin(), dbusName.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800546 dbusName.end(), illegalDbusMemberRegex, "_");
James Feistd58879a2019-09-11 11:26:07 -0700547
548 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
549 createInterface(objServer, path + "/" + dbusName,
550 "xyz.openbmc_project.Configuration." + *type,
James Feist02d2b932020-02-06 16:28:48 -0800551 board, true);
James Feist68500ff2018-08-08 15:40:42 -0700552 // permission is read-write, as since we just created it, must be
553 // runtime modifiable
554 populateInterfaceFromJson(
555 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700556 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700557 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700558 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700559 });
560 iface->initialize();
561}
562
James Feista465ccc2019-02-08 12:51:01 -0800563void postToDbus(const nlohmann::json& newConfiguration,
564 nlohmann::json& systemConfiguration,
565 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800566
James Feist1b2e2242018-01-30 13:45:19 -0800567{
James Feist97a63f12018-05-17 13:50:57 -0700568 // iterate through boards
Patrick Williams2594d362022-09-28 06:46:24 -0500569 for (const auto& [boardId, boardConfig] : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800570 {
Andrew Jeffery13132df2022-03-25 13:29:41 +1030571 std::string boardKey = boardConfig["Name"];
572 std::string boardKeyOrig = boardConfig["Name"];
573 std::string jsonPointerPath = "/" + boardId;
James Feist97a63f12018-05-17 13:50:57 -0700574 // loop through newConfiguration, but use values from system
575 // configuration to be able to modify via dbus later
Andrew Jeffery13132df2022-03-25 13:29:41 +1030576 auto boardValues = systemConfiguration[boardId];
James Feistd63d18a2018-07-19 15:23:45 -0700577 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800578 std::string boardType;
579 if (findBoardType != boardValues.end() &&
580 findBoardType->type() == nlohmann::json::value_t::string)
581 {
582 boardType = findBoardType->get<std::string>();
583 std::regex_replace(boardType.begin(), boardType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800584 boardType.end(), illegalDbusMemberRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800585 }
586 else
587 {
588 std::cerr << "Unable to find type for " << boardKey
589 << " reverting to Chassis.\n";
590 boardType = "Chassis";
591 }
James Feist11be6672018-04-06 14:05:32 -0700592 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800593
594 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800595 illegalDbusMemberRegex, "_");
596 std::string boardName = "/xyz/openbmc_project/inventory/system/";
597 boardName += boardtypeLower;
598 boardName += "/";
599 boardName += boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800600
James Feistd58879a2019-09-11 11:26:07 -0700601 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
602 createInterface(objServer, boardName,
603 "xyz.openbmc_project.Inventory.Item", boardKey);
James Feist68500ff2018-08-08 15:40:42 -0700604
James Feistd58879a2019-09-11 11:26:07 -0700605 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
606 createInterface(objServer, boardName,
607 "xyz.openbmc_project.Inventory.Item." + boardType,
608 boardKeyOrig);
James Feist11be6672018-04-06 14:05:32 -0700609
James Feist68500ff2018-08-08 15:40:42 -0700610 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700611 objServer, boardKeyOrig);
James Feist68500ff2018-08-08 15:40:42 -0700612
James Feist97a63f12018-05-17 13:50:57 -0700613 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700614 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700615 jsonPointerPath += "/";
616 // iterate through board properties
Patrick Williams2594d362022-09-28 06:46:24 -0500617 for (const auto& [propName, propValue] : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700618 {
Andrew Jefferya96950d2022-03-25 13:32:46 +1030619 if (propValue.type() == nlohmann::json::value_t::object)
James Feist11be6672018-04-06 14:05:32 -0700620 {
James Feistd58879a2019-09-11 11:26:07 -0700621 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
Andrew Jefferya96950d2022-03-25 13:32:46 +1030622 createInterface(objServer, boardName, propName,
James Feistd58879a2019-09-11 11:26:07 -0700623 boardKeyOrig);
624
James Feistc6248a52018-08-14 10:09:45 -0700625 populateInterfaceFromJson(systemConfiguration,
Andrew Jefferya96950d2022-03-25 13:32:46 +1030626 jsonPointerPath + propName, iface,
627 propValue, objServer);
James Feist11be6672018-04-06 14:05:32 -0700628 }
629 }
James Feist97a63f12018-05-17 13:50:57 -0700630
James Feist1e3e6982018-08-03 16:09:28 -0700631 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800632 if (exposes == boardValues.end())
633 {
634 continue;
635 }
James Feist97a63f12018-05-17 13:50:57 -0700636 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700637 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700638
639 // store the board level pointer so we can modify it on the way down
640 std::string jsonPointerPathBoard = jsonPointerPath;
641 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -0800642 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -0800643 {
James Feist97a63f12018-05-17 13:50:57 -0700644 exposesIndex++;
645 jsonPointerPath = jsonPointerPathBoard;
646 jsonPointerPath += std::to_string(exposesIndex);
647
James Feistd63d18a2018-07-19 15:23:45 -0700648 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800649 if (findName == item.end())
650 {
651 std::cerr << "cannot find name in field " << item << "\n";
652 continue;
653 }
James Feist1e3e6982018-08-03 16:09:28 -0700654 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -0800655 // if status is not found it is assumed to be status = 'okay'
656 if (findStatus != item.end())
657 {
658 if (*findStatus == "disabled")
659 {
660 continue;
661 }
662 }
James Feistd63d18a2018-07-19 15:23:45 -0700663 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800664 std::string itemType;
665 if (findType != item.end())
666 {
667 itemType = findType->get<std::string>();
668 std::regex_replace(itemType.begin(), itemType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800669 itemType.end(), illegalDbusPathRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800670 }
671 else
672 {
673 itemType = "unknown";
674 }
675 std::string itemName = findName->get<std::string>();
676 std::regex_replace(itemName.begin(), itemName.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800677 itemName.end(), illegalDbusMemberRegex, "_");
678 std::string ifacePath = boardName;
679 ifacePath += "/";
680 ifacePath += itemName;
James Feistc6248a52018-08-14 10:09:45 -0700681
James Feistd58879a2019-09-11 11:26:07 -0700682 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
Ed Tanous07d467b2021-02-23 14:48:37 -0800683 createInterface(objServer, ifacePath,
James Feistd58879a2019-09-11 11:26:07 -0700684 "xyz.openbmc_project.Configuration." + itemType,
685 boardKeyOrig);
James Feist1b2e2242018-01-30 13:45:19 -0800686
Sui Chen74ebe592022-09-13 10:22:03 -0700687 if (itemType == "BMC")
688 {
689 std::shared_ptr<sdbusplus::asio::dbus_interface> bmcIface =
690 createInterface(objServer, ifacePath,
691 "xyz.openbmc_project.Inventory.Item.Bmc",
692 boardKeyOrig);
693 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
694 bmcIface, item, objServer,
695 getPermission(itemType));
696 }
697
James Feist97a63f12018-05-17 13:50:57 -0700698 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700699 itemIface, item, objServer,
700 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -0800701
Patrick Williams2594d362022-09-28 06:46:24 -0500702 for (const auto& [name, config] : item.items())
James Feist1b2e2242018-01-30 13:45:19 -0800703 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030704 jsonPointerPath = jsonPointerPathBoard;
705 jsonPointerPath.append(std::to_string(exposesIndex))
706 .append("/")
707 .append(name);
708 if (config.type() == nlohmann::json::value_t::object)
James Feist1b2e2242018-01-30 13:45:19 -0800709 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030710 std::string ifaceName =
711 "xyz.openbmc_project.Configuration.";
712 ifaceName.append(itemType).append(".").append(name);
James Feist97a63f12018-05-17 13:50:57 -0700713
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030714 std::shared_ptr<sdbusplus::asio::dbus_interface>
715 objectIface = createInterface(objServer, ifacePath,
716 ifaceName, boardKeyOrig);
717
718 populateInterfaceFromJson(
719 systemConfiguration, jsonPointerPath, objectIface,
720 config, objServer, getPermission(name));
James Feist1b2e2242018-01-30 13:45:19 -0800721 }
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030722 else if (config.type() == nlohmann::json::value_t::array)
James Feist1b2e2242018-01-30 13:45:19 -0800723 {
724 size_t index = 0;
Ed Tanous3013fb42022-07-09 08:27:06 -0700725 if (config.empty())
James Feist1b2e2242018-01-30 13:45:19 -0800726 {
James Feist8f2710a2018-05-09 17:18:55 -0700727 continue;
728 }
729 bool isLegal = true;
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030730 auto type = config[0].type();
James Feist8f2710a2018-05-09 17:18:55 -0700731 if (type != nlohmann::json::value_t::object)
732 {
733 continue;
734 }
735
736 // verify legal json
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030737 for (const auto& arrayItem : config)
James Feist8f2710a2018-05-09 17:18:55 -0700738 {
739 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -0800740 {
James Feist8f2710a2018-05-09 17:18:55 -0700741 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -0800742 break;
743 }
James Feist8f2710a2018-05-09 17:18:55 -0700744 }
745 if (!isLegal)
746 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030747 std::cerr << "dbus format error" << config << "\n";
James Feist8f2710a2018-05-09 17:18:55 -0700748 break;
749 }
750
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030751 for (auto& arrayItem : config)
James Feist8f2710a2018-05-09 17:18:55 -0700752 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030753 std::string ifaceName =
754 "xyz.openbmc_project.Configuration.";
755 ifaceName.append(itemType).append(".").append(name);
756 ifaceName.append(std::to_string(index));
James Feist97a63f12018-05-17 13:50:57 -0700757
James Feistd58879a2019-09-11 11:26:07 -0700758 std::shared_ptr<sdbusplus::asio::dbus_interface>
759 objectIface = createInterface(
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030760 objServer, ifacePath, ifaceName, boardKeyOrig);
James Feistd58879a2019-09-11 11:26:07 -0700761
James Feistc6248a52018-08-14 10:09:45 -0700762 populateInterfaceFromJson(
763 systemConfiguration,
764 jsonPointerPath + "/" + std::to_string(index),
765 objectIface, arrayItem, objServer,
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030766 getPermission(name));
James Feistbb43d022018-06-12 15:44:33 -0700767 index++;
James Feist1b2e2242018-01-30 13:45:19 -0800768 }
769 }
770 }
771 }
772 }
773}
774
James Feist8f2710a2018-05-09 17:18:55 -0700775// reads json files out of the filesystem
Andrew Jefferyf3311792022-03-29 22:09:00 +1030776bool loadConfigurations(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -0800777{
778 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -0800779 std::vector<std::filesystem::path> jsonPaths;
Andrew Jefferya9c58922021-06-01 09:28:59 +0930780 if (!findFiles(
781 std::vector<std::filesystem::path>{configurationDirectory,
782 hostConfigurationDirectory},
783 R"(.*\.json)", jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -0800784 {
785 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -0700786 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -0800787 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800788 }
James Feistb4383f42018-08-06 16:54:10 -0700789
790 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
791 globalSchema);
792 if (!schemaStream.good())
793 {
794 std::cerr
795 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
796 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -0800797 return false;
James Feistb4383f42018-08-06 16:54:10 -0700798 }
799 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
800 if (schema.is_discarded())
801 {
802 std::cerr
803 << "Illegal schema file detected, cannot validate JSON, exiting\n";
804 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -0800805 return false;
James Feistb4383f42018-08-06 16:54:10 -0700806 }
807
James Feista465ccc2019-02-08 12:51:01 -0800808 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -0800809 {
810 std::ifstream jsonStream(jsonPath.c_str());
811 if (!jsonStream.good())
812 {
813 std::cerr << "unable to open " << jsonPath.string() << "\n";
814 continue;
815 }
816 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
817 if (data.is_discarded())
818 {
819 std::cerr << "syntax error in " << jsonPath.string() << "\n";
820 continue;
821 }
James Feist8da99192019-01-24 08:20:16 -0800822 /*
823 * todo(james): reenable this once less things are in flight
824 *
James Feistb4383f42018-08-06 16:54:10 -0700825 if (!validateJson(schema, data))
826 {
827 std::cerr << "Error validating " << jsonPath.string() << "\n";
828 continue;
829 }
James Feist8da99192019-01-24 08:20:16 -0800830 */
James Feistb4383f42018-08-06 16:54:10 -0700831
James Feist3cb5fec2018-01-23 14:41:51 -0800832 if (data.type() == nlohmann::json::value_t::array)
833 {
James Feista465ccc2019-02-08 12:51:01 -0800834 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -0800835 {
836 configurations.emplace_back(d);
837 }
838 }
839 else
840 {
841 configurations.emplace_back(data);
842 }
843 }
Ed Tanous072e25d2018-12-16 21:45:20 -0800844 return true;
James Feist75fdeeb2018-02-20 14:26:16 -0800845}
James Feist3cb5fec2018-01-23 14:41:51 -0800846
Andrew Jeffery55192932022-03-24 12:29:27 +1030847static bool deviceRequiresPowerOn(const nlohmann::json& entity)
848{
849 auto powerState = entity.find("PowerState");
Andrew Jefferyb6209442022-03-24 12:36:20 +1030850 if (powerState == entity.end())
Andrew Jeffery55192932022-03-24 12:29:27 +1030851 {
Andrew Jefferyb6209442022-03-24 12:36:20 +1030852 return false;
Andrew Jeffery55192932022-03-24 12:29:27 +1030853 }
854
Ed Tanous3013fb42022-07-09 08:27:06 -0700855 const auto* ptr = powerState->get_ptr<const std::string*>();
856 if (ptr == nullptr)
Andrew Jefferyb6209442022-03-24 12:36:20 +1030857 {
858 return false;
859 }
860
861 return *ptr == "On" || *ptr == "BiosPost";
Andrew Jeffery55192932022-03-24 12:29:27 +1030862}
863
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030864static void pruneDevice(const nlohmann::json& systemConfiguration,
865 const bool powerOff, const bool scannedPowerOff,
866 const std::string& name, const nlohmann::json& device)
867{
868 if (systemConfiguration.contains(name))
869 {
870 return;
871 }
872
Andrew Jeffery4db38bc2022-03-24 13:42:41 +1030873 if (deviceRequiresPowerOn(device) && (powerOff || scannedPowerOff))
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030874 {
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030875 return;
876 }
877
878 logDeviceRemoved(device);
879}
880
James Feistb1728ca2020-04-30 15:40:55 -0700881void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -0700882 nlohmann::json& systemConfiguration)
883{
884 static bool scannedPowerOff = false;
885 static bool scannedPowerOn = false;
886
James Feistfb00f392019-06-25 14:16:48 -0700887 if (systemConfiguration.empty() || lastJson.empty())
888 {
889 return; // not ready yet
890 }
James Feist1df06a42019-04-11 14:23:04 -0700891 if (scannedPowerOn)
892 {
893 return;
894 }
895
896 if (!isPowerOn() && scannedPowerOff)
897 {
898 return;
899 }
900
James Feistb1728ca2020-04-30 15:40:55 -0700901 timer.expires_after(std::chrono::seconds(10));
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030902 timer.async_wait(
903 [&systemConfiguration](const boost::system::error_code& ec) {
904 if (ec == boost::asio::error::operation_aborted)
James Feist1a996582019-05-14 15:10:06 -0700905 {
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030906 return;
James Feist1df06a42019-04-11 14:23:04 -0700907 }
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030908
909 bool powerOff = !isPowerOn();
Andrew Jefferya82c55d2022-03-24 14:17:28 +1030910 for (const auto& [name, device] : lastJson.items())
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030911 {
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030912 pruneDevice(systemConfiguration, powerOff, scannedPowerOff,
913 name, device);
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030914 }
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030915
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030916 scannedPowerOff = true;
917 if (!powerOff)
918 {
919 scannedPowerOn = true;
920 }
921 });
James Feist1df06a42019-04-11 14:23:04 -0700922}
923
Andrew Jeffery2f750d22022-03-24 14:32:57 +1030924static std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
925 getDeviceInterfaces(const nlohmann::json& device)
926{
927 return inventory[device["Name"].get<std::string>()];
928}
929
Andrew Jeffery0d0c1462022-03-24 15:26:39 +1030930static void pruneConfiguration(nlohmann::json& systemConfiguration,
931 sdbusplus::asio::object_server& objServer,
932 bool powerOff, const std::string& name,
933 const nlohmann::json& device)
934{
935 if (powerOff && deviceRequiresPowerOn(device))
936 {
937 // power not on yet, don't know if it's there or not
938 return;
939 }
940
941 auto& ifaces = getDeviceInterfaces(device);
942 for (auto& iface : ifaces)
943 {
944 auto sharedPtr = iface.lock();
945 if (!!sharedPtr)
946 {
947 objServer.remove_interface(sharedPtr);
948 }
949 }
950
951 ifaces.clear();
952 systemConfiguration.erase(name);
953 logDeviceRemoved(device);
954}
955
Andrew Jeffery7c4cbbe2022-03-24 15:27:10 +1030956static void deriveNewConfiguration(const nlohmann::json& oldConfiguration,
957 nlohmann::json& newConfiguration)
958{
959 for (auto it = newConfiguration.begin(); it != newConfiguration.end();)
960 {
961 auto findKey = oldConfiguration.find(it.key());
962 if (findKey != oldConfiguration.end())
963 {
964 it = newConfiguration.erase(it);
965 }
966 else
967 {
968 it++;
969 }
970 }
971}
972
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030973static void publishNewConfiguration(
974 const size_t& instance, const size_t count,
975 boost::asio::steady_timer& timer, nlohmann::json& systemConfiguration,
976 // Gerrit discussion:
977 // https://gerrit.openbmc-project.xyz/c/openbmc/entity-manager/+/52316/6
978 //
979 // Discord discussion:
980 // https://discord.com/channels/775381525260664832/867820390406422538/958048437729910854
981 //
982 // NOLINTNEXTLINE(performance-unnecessary-value-param)
983 const nlohmann::json newConfiguration,
984 sdbusplus::asio::object_server& objServer)
985{
986 loadOverlays(newConfiguration);
987
988 io.post([systemConfiguration]() {
989 if (!writeJsonFiles(systemConfiguration))
990 {
991 std::cerr << "Error writing json files\n";
992 }
993 });
994
995 io.post([&instance, count, &timer, newConfiguration, &systemConfiguration,
996 &objServer]() {
997 postToDbus(newConfiguration, systemConfiguration, objServer);
998 if (count == instance)
999 {
1000 startRemovedTimer(timer, systemConfiguration);
1001 }
1002 });
1003}
1004
James Feist8f2710a2018-05-09 17:18:55 -07001005// main properties changed entry
James Feist4dc617b2020-05-01 09:54:47 -07001006void propertiesChangedCallback(nlohmann::json& systemConfiguration,
1007 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001008{
James Feist2539ccd2020-05-01 16:15:08 -07001009 static bool inProgress = false;
James Feistb1728ca2020-04-30 15:40:55 -07001010 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001011 static size_t instance = 0;
1012 instance++;
1013 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001014
James Feistb1728ca2020-04-30 15:40:55 -07001015 timer.expires_after(std::chrono::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001016
1017 // setup an async wait as we normally get flooded with new requests
James Feist4dc617b2020-05-01 09:54:47 -07001018 timer.async_wait([&systemConfiguration, &objServer,
James Feist899e17f2019-09-13 11:46:29 -07001019 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001020 if (ec == boost::asio::error::operation_aborted)
1021 {
1022 // we were cancelled
1023 return;
1024 }
Ed Tanous07d467b2021-02-23 14:48:37 -08001025 if (ec)
James Feist8f2710a2018-05-09 17:18:55 -07001026 {
1027 std::cerr << "async wait error " << ec << "\n";
1028 return;
1029 }
1030
James Feist2539ccd2020-05-01 16:15:08 -07001031 if (inProgress)
1032 {
1033 propertiesChangedCallback(systemConfiguration, objServer);
1034 return;
1035 }
1036 inProgress = true;
1037
James Feist8f2710a2018-05-09 17:18:55 -07001038 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001039 auto missingConfigurations = std::make_shared<nlohmann::json>();
1040 *missingConfigurations = systemConfiguration;
1041
James Feist8f2710a2018-05-09 17:18:55 -07001042 std::list<nlohmann::json> configurations;
Andrew Jefferyf3311792022-03-29 22:09:00 +10301043 if (!loadConfigurations(configurations))
James Feist8f2710a2018-05-09 17:18:55 -07001044 {
Andrew Jefferyf3311792022-03-29 22:09:00 +10301045 std::cerr << "Could not load configurations\n";
James Feist2539ccd2020-05-01 16:15:08 -07001046 inProgress = false;
James Feist8f2710a2018-05-09 17:18:55 -07001047 return;
1048 }
1049
1050 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001051 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001052 objServer,
1053 [&systemConfiguration, &objServer, count, oldConfiguration,
1054 missingConfigurations]() {
James Feist899e17f2019-09-13 11:46:29 -07001055 // this is something that since ac has been applied to the bmc
1056 // we saw, and we no longer see it
1057 bool powerOff = !isPowerOn();
Andrew Jeffery6cbbc092022-03-24 14:35:14 +10301058 for (const auto& [name, device] :
1059 missingConfigurations->items())
James Feist899e17f2019-09-13 11:46:29 -07001060 {
Andrew Jeffery0d0c1462022-03-24 15:26:39 +10301061 pruneConfiguration(systemConfiguration, objServer, powerOff,
1062 name, device);
James Feist899e17f2019-09-13 11:46:29 -07001063 }
1064
James Feist8f2710a2018-05-09 17:18:55 -07001065 nlohmann::json newConfiguration = systemConfiguration;
Andrew Jeffery7c4cbbe2022-03-24 15:27:10 +10301066
1067 deriveNewConfiguration(oldConfiguration, newConfiguration);
1068
Andrew Jefferyace306d2022-03-25 13:18:57 +10301069 for (const auto& [_, device] : newConfiguration.items())
James Feist899e17f2019-09-13 11:46:29 -07001070 {
Andrew Jefferyace306d2022-03-25 13:18:57 +10301071 logDeviceAdded(device);
James Feist899e17f2019-09-13 11:46:29 -07001072 }
1073
James Feist2539ccd2020-05-01 16:15:08 -07001074 inProgress = false;
1075
Andrew Jefferye35d0ac2022-03-24 15:53:13 +10301076 io.post(std::bind_front(
1077 publishNewConfiguration, std::ref(instance), count,
1078 std::ref(timer), std::ref(systemConfiguration),
1079 newConfiguration, std::ref(objServer)));
James Feist8f2710a2018-05-09 17:18:55 -07001080 });
1081 perfScan->run();
1082 });
James Feist75fdeeb2018-02-20 14:26:16 -08001083}
1084
James Feist98132792019-07-09 13:29:09 -07001085int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001086{
1087 // setup connection to dbus
Ed Tanous07d467b2021-02-23 14:48:37 -08001088 systemBus = std::make_shared<sdbusplus::asio::connection>(io);
1089 systemBus->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001090
Nan Zhoua3315672022-09-20 19:48:14 +00001091 // The EntityManager object itself doesn't expose any properties.
1092 // No need to set up ObjectManager for the |EntityManager| object.
1093 sdbusplus::asio::object_server objServer(systemBus, /*skipManager=*/true);
1094
1095 // All other objects that EntityManager currently support are under the
1096 // inventory subtree.
1097 // See the discussion at
1098 // https://discord.com/channels/775381525260664832/1018929092009144380
1099 objServer.add_manager("/xyz/openbmc_project/inventory");
James Feistfd1264a2018-05-03 12:10:00 -07001100
James Feist8f2710a2018-05-09 17:18:55 -07001101 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1102 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1103 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001104
James Feist4131aea2018-03-09 09:47:30 -08001105 // to keep reference to the match / filter objects so they don't get
1106 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001107
1108 nlohmann::json systemConfiguration = nlohmann::json::object();
1109
Brad Bishopc76af0f2020-12-04 13:50:23 -05001110 // We need a poke from DBus for static providers that create all their
1111 // objects prior to claiming a well-known name, and thus don't emit any
1112 // org.freedesktop.DBus.Properties signals. Similarly if a process exits
1113 // for any reason, expected or otherwise, we'll need a poke to remove
1114 // entities from DBus.
Patrick Williams2af39222022-07-22 19:26:56 -05001115 sdbusplus::bus::match_t nameOwnerChangedMatch(
1116 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishopc76af0f2020-12-04 13:50:23 -05001117 sdbusplus::bus::match::rules::nameOwnerChanged(),
Patrick Williams7b8786f2022-10-10 10:23:37 -05001118 [&](sdbusplus::message_t& m) {
1119 auto [name, oldOwner, newOwner] =
1120 m.unpack<std::string, std::string, std::string>();
1121
1122 if (name.starts_with(':'))
1123 {
1124 // We should do nothing with unique-name connections.
1125 return;
1126 }
1127
Brad Bishopc76af0f2020-12-04 13:50:23 -05001128 propertiesChangedCallback(systemConfiguration, objServer);
1129 });
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001130 // We also need a poke from DBus when new interfaces are created or
1131 // destroyed.
Patrick Williams2af39222022-07-22 19:26:56 -05001132 sdbusplus::bus::match_t interfacesAddedMatch(
1133 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001134 sdbusplus::bus::match::rules::interfacesAdded(),
Patrick Williams2af39222022-07-22 19:26:56 -05001135 [&](sdbusplus::message_t&) {
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001136 propertiesChangedCallback(systemConfiguration, objServer);
1137 });
Patrick Williams2af39222022-07-22 19:26:56 -05001138 sdbusplus::bus::match_t interfacesRemovedMatch(
1139 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001140 sdbusplus::bus::match::rules::interfacesRemoved(),
Patrick Williams2af39222022-07-22 19:26:56 -05001141 [&](sdbusplus::message_t&) {
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001142 propertiesChangedCallback(systemConfiguration, objServer);
1143 });
Brad Bishopc76af0f2020-12-04 13:50:23 -05001144
James Feist4dc617b2020-05-01 09:54:47 -07001145 io.post(
1146 [&]() { propertiesChangedCallback(systemConfiguration, objServer); });
James Feist4131aea2018-03-09 09:47:30 -08001147
James Feistfd1264a2018-05-03 12:10:00 -07001148 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -07001149 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001150 });
James Feist8f2710a2018-05-09 17:18:55 -07001151 entityIface->initialize();
1152
James Feist1df06a42019-04-11 14:23:04 -07001153 if (fwVersionIsSame())
1154 {
1155 if (std::filesystem::is_regular_file(currentConfiguration))
1156 {
1157 // this file could just be deleted, but it's nice for debug
1158 std::filesystem::create_directory(tempConfigDir);
1159 std::filesystem::remove(lastConfiguration);
1160 std::filesystem::copy(currentConfiguration, lastConfiguration);
1161 std::filesystem::remove(currentConfiguration);
1162
1163 std::ifstream jsonStream(lastConfiguration);
1164 if (jsonStream.good())
1165 {
1166 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1167 if (data.is_discarded())
1168 {
1169 std::cerr << "syntax error in " << lastConfiguration
1170 << "\n";
1171 }
1172 else
1173 {
1174 lastJson = std::move(data);
1175 }
1176 }
1177 else
1178 {
1179 std::cerr << "unable to open " << lastConfiguration << "\n";
1180 }
1181 }
1182 }
1183 else
1184 {
1185 // not an error, just logging at this level to make it in the journal
1186 std::cerr << "Clearing previous configuration\n";
1187 std::filesystem::remove(currentConfiguration);
1188 }
1189
1190 // some boards only show up after power is on, we want to not say they are
1191 // removed until the same state happens
Ed Tanous07d467b2021-02-23 14:48:37 -08001192 setupPowerMatch(systemBus);
James Feist1df06a42019-04-11 14:23:04 -07001193
James Feist1b2e2242018-01-30 13:45:19 -08001194 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001195
1196 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001197}