blob: e4e458e2d73ce5a32bc4f260af14a9284f66f371 [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
Christopher Meisbdaa6b22025-04-02 10:49:02 +020020#include "configuration.hpp"
Brad Bishope45d8c72022-05-25 15:12:53 -040021#include "overlay.hpp"
Christopher Meis26fbbd52025-03-26 14:55:06 +010022#include "perform_scan.hpp"
Benjamin Fairca2eb042022-09-13 06:40:42 +000023#include "topology.hpp"
Brad Bishope45d8c72022-05-25 15:12:53 -040024#include "utils.hpp"
25#include "variant_visitors.hpp"
James Feist481c5d52019-08-13 14:40:40 -070026
James Feist11be6672018-04-06 14:05:32 -070027#include <boost/algorithm/string/case_conv.hpp>
James Feistf5125b02019-06-06 11:27:43 -070028#include <boost/algorithm/string/classification.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080029#include <boost/algorithm/string/predicate.hpp>
30#include <boost/algorithm/string/replace.hpp>
James Feistf5125b02019-06-06 11:27:43 -070031#include <boost/algorithm/string/split.hpp>
James Feist02d2b932020-02-06 16:28:48 -080032#include <boost/asio/io_context.hpp>
Ed Tanous49a888c2023-03-06 13:44:51 -080033#include <boost/asio/post.hpp>
James Feistb1728ca2020-04-30 15:40:55 -070034#include <boost/asio/steady_timer.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080035#include <boost/container/flat_map.hpp>
36#include <boost/container/flat_set.hpp>
James Feistf5125b02019-06-06 11:27:43 -070037#include <boost/range/iterator_range.hpp>
James Feist8c505da2020-05-28 10:06:33 -070038#include <nlohmann/json.hpp>
39#include <sdbusplus/asio/connection.hpp>
40#include <sdbusplus/asio/object_server.hpp>
41
Igor Kononenko9fd87e52020-10-06 01:18:17 +030042#include <charconv>
James Feist637b3ef2019-04-15 16:35:30 -070043#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080044#include <fstream>
Andrew Jefferye35d0ac2022-03-24 15:53:13 +103045#include <functional>
James Feista465ccc2019-02-08 12:51:01 -080046#include <iostream>
Andrew Jeffery666583b2021-12-01 15:50:12 +103047#include <map>
James Feista465ccc2019-02-08 12:51:01 -080048#include <regex>
James Feista465ccc2019-02-08 12:51:01 -080049#include <variant>
James Feist1df06a42019-04-11 14:23:04 -070050constexpr const char* tempConfigDir = "/tmp/configuration/";
51constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
James Feistf1b14142019-04-10 15:22:09 -070052
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +020053static constexpr std::array<const char*, 6> settableInterfaces = {
54 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
James Feist68500ff2018-08-08 15:40:42 -070055using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080056 std::variant<std::vector<std::string>, std::vector<double>, std::string,
57 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
58 uint16_t, uint8_t, bool>;
James Feist3cb5fec2018-01-23 14:41:51 -080059
Ed Tanousfc171422024-04-04 17:18:16 -070060// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
James Feistd58879a2019-09-11 11:26:07 -070061// store reference to all interfaces so we can destroy them later
62boost::container::flat_map<
James Feist02d2b932020-02-06 16:28:48 -080063 std::string, std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>>
James Feistd58879a2019-09-11 11:26:07 -070064 inventory;
65
James Feist3cb5fec2018-01-23 14:41:51 -080066// todo: pass this through nicer
Ed Tanous07d467b2021-02-23 14:48:37 -080067std::shared_ptr<sdbusplus::asio::connection> systemBus;
Andrew Jeffery47af65a2021-12-01 14:16:31 +103068nlohmann::json lastJson;
Matt Spinler6eb60972023-08-14 16:36:20 -050069Topology topology;
James Feist3cb5fec2018-01-23 14:41:51 -080070
James Feist02d2b932020-02-06 16:28:48 -080071boost::asio::io_context io;
Ed Tanousfc171422024-04-04 17:18:16 -070072// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
James Feist02d2b932020-02-06 16:28:48 -080073
Ed Tanous07d467b2021-02-23 14:48:37 -080074const std::regex illegalDbusPathRegex("[^A-Za-z0-9_.]");
75const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -080076
John Edward Broadbentd97c6312023-10-26 20:32:07 +000077void tryIfaceInitialize(std::shared_ptr<sdbusplus::asio::dbus_interface>& iface)
78{
79 try
80 {
81 iface->initialize();
82 }
83 catch (std::exception& e)
84 {
85 std::cerr << "Unable to initialize dbus interface : " << e.what()
86 << "\n"
87 << "object Path : " << iface->get_object_path() << "\n"
88 << "interface name : " << iface->get_interface_name() << "\n";
89 }
90}
91
Patrick Williams5a807032025-03-03 11:20:39 -050092static std::shared_ptr<sdbusplus::asio::dbus_interface> createInterface(
93 sdbusplus::asio::object_server& objServer, const std::string& path,
94 const std::string& interface, const std::string& parent,
95 bool checkNull = false)
James Feistd58879a2019-09-11 11:26:07 -070096{
James Feist02d2b932020-02-06 16:28:48 -080097 // on first add we have no reason to check for null before add, as there
98 // won't be any. For dynamically added interfaces, we check for null so that
99 // a constant delete/add will not create a memory leak
100
101 auto ptr = objServer.add_interface(path, interface);
102 auto& dataVector = inventory[parent];
103 if (checkNull)
104 {
105 auto it = std::find_if(dataVector.begin(), dataVector.end(),
106 [](const auto& p) { return p.expired(); });
107 if (it != dataVector.end())
108 {
109 *it = ptr;
110 return ptr;
111 }
112 }
113 dataVector.emplace_back(ptr);
114 return ptr;
James Feistd58879a2019-09-11 11:26:07 -0700115}
116
James Feistebcc26b2019-03-22 12:30:43 -0700117// template function to add array as dbus property
118template <typename PropertyType>
119void addArrayToDbus(const std::string& name, const nlohmann::json& array,
120 sdbusplus::asio::dbus_interface* iface,
121 sdbusplus::asio::PropertyPermission permission,
122 nlohmann::json& systemConfiguration,
123 const std::string& jsonPointerString)
124{
125 std::vector<PropertyType> values;
126 for (const auto& property : array)
127 {
128 auto ptr = property.get_ptr<const PropertyType*>();
129 if (ptr != nullptr)
130 {
131 values.emplace_back(*ptr);
132 }
133 }
134
135 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
136 {
137 iface->register_property(name, values);
138 }
139 else
140 {
141 iface->register_property(
142 name, values,
143 [&systemConfiguration,
144 jsonPointerString{std::string(jsonPointerString)}](
145 const std::vector<PropertyType>& newVal,
146 std::vector<PropertyType>& val) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400147 val = newVal;
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200148 if (!configuration::setJsonFromPointer(jsonPointerString, val,
149 systemConfiguration))
Patrick Williamsb7077432024-08-16 15:22:21 -0400150 {
151 std::cerr << "error setting json field\n";
152 return -1;
153 }
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200154 if (!configuration::writeJsonFiles(systemConfiguration))
Patrick Williamsb7077432024-08-16 15:22:21 -0400155 {
156 std::cerr << "error setting json file\n";
157 return -1;
158 }
159 return 1;
160 });
James Feistebcc26b2019-03-22 12:30:43 -0700161 }
162}
163
James Feistbb43d022018-06-12 15:44:33 -0700164template <typename PropertyType>
Ed Tanous3013fb42022-07-09 08:27:06 -0700165void addProperty(const std::string& name, const PropertyType& value,
James Feista465ccc2019-02-08 12:51:01 -0800166 sdbusplus::asio::dbus_interface* iface,
167 nlohmann::json& systemConfiguration,
168 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700169 sdbusplus::asio::PropertyPermission permission)
170{
171 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
172 {
Ed Tanous3013fb42022-07-09 08:27:06 -0700173 iface->register_property(name, value);
James Feistbb43d022018-06-12 15:44:33 -0700174 return;
175 }
James Feist68500ff2018-08-08 15:40:42 -0700176 iface->register_property(
Ed Tanous3013fb42022-07-09 08:27:06 -0700177 name, value,
James Feist68500ff2018-08-08 15:40:42 -0700178 [&systemConfiguration,
179 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800180 const PropertyType& newVal, PropertyType& val) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400181 val = newVal;
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200182 if (!configuration::setJsonFromPointer(jsonPointerString, val,
183 systemConfiguration))
Patrick Williamsb7077432024-08-16 15:22:21 -0400184 {
185 std::cerr << "error setting json field\n";
186 return -1;
187 }
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200188 if (!configuration::writeJsonFiles(systemConfiguration))
Patrick Williamsb7077432024-08-16 15:22:21 -0400189 {
190 std::cerr << "error setting json file\n";
191 return -1;
192 }
193 return 1;
194 });
James Feistc6248a52018-08-14 10:09:45 -0700195}
196
197void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800198 const std::string& jsonPointerPath,
199 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
200 sdbusplus::asio::object_server& objServer,
201 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700202{
203 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
Patrick Williamsb7077432024-08-16 15:22:21 -0400204 iface->register_method(
205 "Delete", [&objServer, &systemConfiguration, interface,
206 jsonPointerPath{std::string(jsonPointerPath)}]() {
207 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
208 interface.lock();
209 if (!dbusInterface)
210 {
211 // this technically can't happen as the pointer is pointing to
212 // us
213 throw DBusInternalError();
214 }
215 nlohmann::json::json_pointer ptr(jsonPointerPath);
216 systemConfiguration[ptr] = nullptr;
James Feistc6248a52018-08-14 10:09:45 -0700217
Patrick Williamsb7077432024-08-16 15:22:21 -0400218 // todo(james): dig through sdbusplus to find out why we can't
219 // delete it in a method call
220 boost::asio::post(io, [&objServer, dbusInterface]() mutable {
221 objServer.remove_interface(dbusInterface);
222 });
223
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200224 if (!configuration::writeJsonFiles(systemConfiguration))
Patrick Williamsb7077432024-08-16 15:22:21 -0400225 {
226 std::cerr << "error setting json file\n";
227 throw DBusInternalError();
228 }
James Feist68500ff2018-08-08 15:40:42 -0700229 });
James Feistbb43d022018-06-12 15:44:33 -0700230}
231
James Feist1b2e2242018-01-30 13:45:19 -0800232// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700233void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800234 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
235 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
236 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700237 sdbusplus::asio::PropertyPermission permission =
238 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800239{
Patrick Williams2594d362022-09-28 06:46:24 -0500240 for (const auto& [key, value] : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800241 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030242 auto type = value.type();
James Feist8f2710a2018-05-09 17:18:55 -0700243 bool array = false;
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030244 if (value.type() == nlohmann::json::value_t::array)
James Feist8f2710a2018-05-09 17:18:55 -0700245 {
246 array = true;
Ed Tanous3013fb42022-07-09 08:27:06 -0700247 if (value.empty())
James Feist8f2710a2018-05-09 17:18:55 -0700248 {
249 continue;
250 }
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030251 type = value[0].type();
James Feist8f2710a2018-05-09 17:18:55 -0700252 bool isLegal = true;
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030253 for (const auto& arrayItem : value)
James Feist8f2710a2018-05-09 17:18:55 -0700254 {
255 if (arrayItem.type() != type)
256 {
257 isLegal = false;
258 break;
259 }
260 }
261 if (!isLegal)
262 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030263 std::cerr << "dbus format error" << value << "\n";
James Feist8f2710a2018-05-09 17:18:55 -0700264 continue;
265 }
James Feista218ddb2019-04-11 14:01:31 -0700266 }
267 if (type == nlohmann::json::value_t::object)
268 {
269 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700270 }
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030271
272 std::string path = jsonPointerPath;
273 path.append("/").append(key);
James Feistbb43d022018-06-12 15:44:33 -0700274 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
275 {
276 // all setable numbers are doubles as it is difficult to always
277 // create a configuration file with all whole numbers as decimals
278 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700279 if (array)
280 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030281 if (value[0].is_number())
James Feistebcc26b2019-03-22 12:30:43 -0700282 {
283 type = nlohmann::json::value_t::number_float;
284 }
285 }
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030286 else if (value.is_number())
James Feistbb43d022018-06-12 15:44:33 -0700287 {
288 type = nlohmann::json::value_t::number_float;
289 }
290 }
291
James Feist8f2710a2018-05-09 17:18:55 -0700292 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800293 {
James Feist9eb0b582018-04-27 12:15:46 -0700294 case (nlohmann::json::value_t::boolean):
295 {
James Feist8f2710a2018-05-09 17:18:55 -0700296 if (array)
297 {
298 // todo: array of bool isn't detected correctly by
299 // sdbusplus, change it to numbers
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030300 addArrayToDbus<uint64_t>(key, value, iface.get(),
301 permission, systemConfiguration,
302 path);
James Feist8f2710a2018-05-09 17:18:55 -0700303 }
James Feistbb43d022018-06-12 15:44:33 -0700304
James Feist97a63f12018-05-17 13:50:57 -0700305 else
306 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030307 addProperty(key, value.get<bool>(), iface.get(),
308 systemConfiguration, path, permission);
James Feist97a63f12018-05-17 13:50:57 -0700309 }
James Feist9eb0b582018-04-27 12:15:46 -0700310 break;
311 }
312 case (nlohmann::json::value_t::number_integer):
313 {
James Feist8f2710a2018-05-09 17:18:55 -0700314 if (array)
315 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030316 addArrayToDbus<int64_t>(key, value, iface.get(), permission,
Andrew Jeffery029ee282022-03-25 13:11:36 +1030317 systemConfiguration, path);
James Feist97a63f12018-05-17 13:50:57 -0700318 }
319 else
320 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030321 addProperty(key, value.get<int64_t>(), iface.get(),
322 systemConfiguration, path,
James Feistbb43d022018-06-12 15:44:33 -0700323 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700324 }
James Feist9eb0b582018-04-27 12:15:46 -0700325 break;
326 }
327 case (nlohmann::json::value_t::number_unsigned):
328 {
James Feist8f2710a2018-05-09 17:18:55 -0700329 if (array)
330 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030331 addArrayToDbus<uint64_t>(key, value, iface.get(),
332 permission, systemConfiguration,
333 path);
James Feist97a63f12018-05-17 13:50:57 -0700334 }
335 else
336 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030337 addProperty(key, value.get<uint64_t>(), iface.get(),
Andrew Jeffery029ee282022-03-25 13:11:36 +1030338 systemConfiguration, path,
James Feistbb43d022018-06-12 15:44:33 -0700339 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700340 }
James Feist9eb0b582018-04-27 12:15:46 -0700341 break;
342 }
343 case (nlohmann::json::value_t::number_float):
344 {
James Feist8f2710a2018-05-09 17:18:55 -0700345 if (array)
346 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030347 addArrayToDbus<double>(key, value, iface.get(), permission,
Andrew Jeffery029ee282022-03-25 13:11:36 +1030348 systemConfiguration, path);
James Feist8f2710a2018-05-09 17:18:55 -0700349 }
James Feistbb43d022018-06-12 15:44:33 -0700350
James Feist97a63f12018-05-17 13:50:57 -0700351 else
352 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030353 addProperty(key, value.get<double>(), iface.get(),
354 systemConfiguration, path, permission);
James Feist97a63f12018-05-17 13:50:57 -0700355 }
James Feist9eb0b582018-04-27 12:15:46 -0700356 break;
357 }
358 case (nlohmann::json::value_t::string):
359 {
James Feist8f2710a2018-05-09 17:18:55 -0700360 if (array)
361 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030362 addArrayToDbus<std::string>(key, value, iface.get(),
363 permission, systemConfiguration,
364 path);
James Feist97a63f12018-05-17 13:50:57 -0700365 }
366 else
367 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030368 addProperty(key, value.get<std::string>(), iface.get(),
369 systemConfiguration, path, permission);
James Feist97a63f12018-05-17 13:50:57 -0700370 }
James Feist9eb0b582018-04-27 12:15:46 -0700371 break;
372 }
James Feist0eb40352019-04-09 14:44:04 -0700373 default:
374 {
James Feista218ddb2019-04-11 14:01:31 -0700375 std::cerr << "Unexpected json type in system configuration "
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030376 << key << ": " << value.type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700377 break;
378 }
James Feist1b2e2242018-01-30 13:45:19 -0800379 }
380 }
James Feistc6248a52018-08-14 10:09:45 -0700381 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
382 {
383 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
384 systemConfiguration);
385 }
John Edward Broadbentd97c6312023-10-26 20:32:07 +0000386 tryIfaceInitialize(iface);
James Feist1b2e2242018-01-30 13:45:19 -0800387}
388
James Feista465ccc2019-02-08 12:51:01 -0800389sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700390{
391 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
392 interface) != settableInterfaces.end()
393 ? sdbusplus::asio::PropertyPermission::readWrite
394 : sdbusplus::asio::PropertyPermission::readOnly;
395}
396
Patrick Williamsb7077432024-08-16 15:22:21 -0400397void createAddObjectMethod(
398 const std::string& jsonPointerPath, const std::string& path,
399 nlohmann::json& systemConfiguration,
400 sdbusplus::asio::object_server& objServer, const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700401{
James Feistd58879a2019-09-11 11:26:07 -0700402 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
403 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700404
405 iface->register_method(
406 "AddObject",
407 [&systemConfiguration, &objServer,
James Feistd58879a2019-09-11 11:26:07 -0700408 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
409 board](const boost::container::flat_map<std::string, JsonVariantType>&
410 data) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400411 nlohmann::json::json_pointer ptr(jsonPointerPath);
412 nlohmann::json& base = systemConfiguration[ptr];
413 auto findExposes = base.find("Exposes");
James Feist68500ff2018-08-08 15:40:42 -0700414
Patrick Williamsb7077432024-08-16 15:22:21 -0400415 if (findExposes == base.end())
James Feist68500ff2018-08-08 15:40:42 -0700416 {
Patrick Williamsb7077432024-08-16 15:22:21 -0400417 throw std::invalid_argument("Entity must have children.");
James Feist68500ff2018-08-08 15:40:42 -0700418 }
419
Patrick Williamsb7077432024-08-16 15:22:21 -0400420 // this will throw invalid-argument to sdbusplus if invalid json
421 nlohmann::json newData{};
422 for (const auto& item : data)
423 {
424 nlohmann::json& newJson = newData[item.first];
425 std::visit(
426 [&newJson](auto&& val) {
427 newJson = std::forward<decltype(val)>(val);
428 },
429 item.second);
430 }
431
432 auto findName = newData.find("Name");
433 auto findType = newData.find("Type");
434 if (findName == newData.end() || findType == newData.end())
435 {
436 throw std::invalid_argument("AddObject missing Name or Type");
437 }
438 const std::string* type = findType->get_ptr<const std::string*>();
439 const std::string* name = findName->get_ptr<const std::string*>();
440 if (type == nullptr || name == nullptr)
441 {
442 throw std::invalid_argument("Type and Name must be a string.");
443 }
444
445 bool foundNull = false;
446 size_t lastIndex = 0;
447 // we add in the "exposes"
448 for (const auto& expose : *findExposes)
449 {
450 if (expose.is_null())
451 {
452 foundNull = true;
453 continue;
454 }
455
456 if (expose["Name"] == *name && expose["Type"] == *type)
457 {
458 throw std::invalid_argument(
459 "Field already in JSON, not adding");
460 }
461
462 if (foundNull)
463 {
464 continue;
465 }
466
467 lastIndex++;
468 }
469
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200470 std::ifstream schemaFile(
471 std::string(configuration::schemaDirectory) + "/" +
472 boost::to_lower_copy(*type) + ".json");
Patrick Williamsb7077432024-08-16 15:22:21 -0400473 // todo(james) we might want to also make a list of 'can add'
474 // interfaces but for now I think the assumption if there is a
475 // schema avaliable that it is allowed to update is fine
476 if (!schemaFile.good())
James Feist68500ff2018-08-08 15:40:42 -0700477 {
478 throw std::invalid_argument(
Patrick Williamsb7077432024-08-16 15:22:21 -0400479 "No schema avaliable, cannot validate.");
James Feist68500ff2018-08-08 15:40:42 -0700480 }
Patrick Williamsb7077432024-08-16 15:22:21 -0400481 nlohmann::json schema =
482 nlohmann::json::parse(schemaFile, nullptr, false, true);
483 if (schema.is_discarded())
484 {
485 std::cerr << "Schema not legal" << *type << ".json\n";
486 throw DBusInternalError();
487 }
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200488 if (!configuration::validateJson(schema, newData))
Patrick Williamsb7077432024-08-16 15:22:21 -0400489 {
490 throw std::invalid_argument("Data does not match schema");
491 }
James Feist02d2b932020-02-06 16:28:48 -0800492 if (foundNull)
493 {
Patrick Williamsb7077432024-08-16 15:22:21 -0400494 findExposes->at(lastIndex) = newData;
James Feist02d2b932020-02-06 16:28:48 -0800495 }
Patrick Williamsb7077432024-08-16 15:22:21 -0400496 else
497 {
498 findExposes->push_back(newData);
499 }
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200500 if (!configuration::writeJsonFiles(systemConfiguration))
Patrick Williamsb7077432024-08-16 15:22:21 -0400501 {
502 std::cerr << "Error writing json files\n";
503 throw DBusInternalError();
504 }
505 std::string dbusName = *name;
James Feist68500ff2018-08-08 15:40:42 -0700506
Patrick Williamsb7077432024-08-16 15:22:21 -0400507 std::regex_replace(dbusName.begin(), dbusName.begin(),
508 dbusName.end(), illegalDbusMemberRegex, "_");
James Feistd58879a2019-09-11 11:26:07 -0700509
Patrick Williamsb7077432024-08-16 15:22:21 -0400510 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
511 createInterface(objServer, path + "/" + dbusName,
512 "xyz.openbmc_project.Configuration." + *type,
513 board, true);
514 // permission is read-write, as since we just created it, must be
515 // runtime modifiable
516 populateInterfaceFromJson(
517 systemConfiguration,
518 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
519 interface, newData, objServer,
520 sdbusplus::asio::PropertyPermission::readWrite);
521 });
John Edward Broadbentd97c6312023-10-26 20:32:07 +0000522 tryIfaceInitialize(iface);
James Feist68500ff2018-08-08 15:40:42 -0700523}
524
James Feista465ccc2019-02-08 12:51:01 -0800525void postToDbus(const nlohmann::json& newConfiguration,
526 nlohmann::json& systemConfiguration,
527 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800528
James Feist1b2e2242018-01-30 13:45:19 -0800529{
Matt Spinler6eb60972023-08-14 16:36:20 -0500530 std::map<std::string, std::string> newBoards; // path -> name
Benjamin Fairca2eb042022-09-13 06:40:42 +0000531
James Feist97a63f12018-05-17 13:50:57 -0700532 // iterate through boards
Patrick Williams2594d362022-09-28 06:46:24 -0500533 for (const auto& [boardId, boardConfig] : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800534 {
Matt Spinler3d1909a2023-08-10 16:39:44 -0500535 std::string boardName = boardConfig["Name"];
536 std::string boardNameOrig = boardConfig["Name"];
Andrew Jeffery13132df2022-03-25 13:29:41 +1030537 std::string jsonPointerPath = "/" + boardId;
James Feist97a63f12018-05-17 13:50:57 -0700538 // loop through newConfiguration, but use values from system
539 // configuration to be able to modify via dbus later
Andrew Jeffery13132df2022-03-25 13:29:41 +1030540 auto boardValues = systemConfiguration[boardId];
James Feistd63d18a2018-07-19 15:23:45 -0700541 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800542 std::string boardType;
543 if (findBoardType != boardValues.end() &&
544 findBoardType->type() == nlohmann::json::value_t::string)
545 {
546 boardType = findBoardType->get<std::string>();
547 std::regex_replace(boardType.begin(), boardType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800548 boardType.end(), illegalDbusMemberRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800549 }
550 else
551 {
Matt Spinler3d1909a2023-08-10 16:39:44 -0500552 std::cerr << "Unable to find type for " << boardName
James Feist1b2e2242018-01-30 13:45:19 -0800553 << " reverting to Chassis.\n";
554 boardType = "Chassis";
555 }
James Feist11be6672018-04-06 14:05:32 -0700556 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800557
Matt Spinler3d1909a2023-08-10 16:39:44 -0500558 std::regex_replace(boardName.begin(), boardName.begin(),
559 boardName.end(), illegalDbusMemberRegex, "_");
560 std::string boardPath = "/xyz/openbmc_project/inventory/system/";
561 boardPath += boardtypeLower;
562 boardPath += "/";
563 boardPath += boardName;
James Feist1b2e2242018-01-30 13:45:19 -0800564
James Feistd58879a2019-09-11 11:26:07 -0700565 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
Matt Spinler3d1909a2023-08-10 16:39:44 -0500566 createInterface(objServer, boardPath,
567 "xyz.openbmc_project.Inventory.Item", boardName);
James Feist68500ff2018-08-08 15:40:42 -0700568
James Feistd58879a2019-09-11 11:26:07 -0700569 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
Matt Spinler3d1909a2023-08-10 16:39:44 -0500570 createInterface(objServer, boardPath,
James Feistd58879a2019-09-11 11:26:07 -0700571 "xyz.openbmc_project.Inventory.Item." + boardType,
Matt Spinler3d1909a2023-08-10 16:39:44 -0500572 boardNameOrig);
James Feist11be6672018-04-06 14:05:32 -0700573
Matt Spinler3d1909a2023-08-10 16:39:44 -0500574 createAddObjectMethod(jsonPointerPath, boardPath, systemConfiguration,
575 objServer, boardNameOrig);
James Feist68500ff2018-08-08 15:40:42 -0700576
James Feist97a63f12018-05-17 13:50:57 -0700577 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700578 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700579 jsonPointerPath += "/";
580 // iterate through board properties
Patrick Williams2594d362022-09-28 06:46:24 -0500581 for (const auto& [propName, propValue] : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700582 {
Andrew Jefferya96950d2022-03-25 13:32:46 +1030583 if (propValue.type() == nlohmann::json::value_t::object)
James Feist11be6672018-04-06 14:05:32 -0700584 {
James Feistd58879a2019-09-11 11:26:07 -0700585 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
Matt Spinler3d1909a2023-08-10 16:39:44 -0500586 createInterface(objServer, boardPath, propName,
587 boardNameOrig);
James Feistd58879a2019-09-11 11:26:07 -0700588
James Feistc6248a52018-08-14 10:09:45 -0700589 populateInterfaceFromJson(systemConfiguration,
Andrew Jefferya96950d2022-03-25 13:32:46 +1030590 jsonPointerPath + propName, iface,
591 propValue, objServer);
James Feist11be6672018-04-06 14:05:32 -0700592 }
593 }
James Feist97a63f12018-05-17 13:50:57 -0700594
James Feist1e3e6982018-08-03 16:09:28 -0700595 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800596 if (exposes == boardValues.end())
597 {
598 continue;
599 }
James Feist97a63f12018-05-17 13:50:57 -0700600 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700601 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700602
603 // store the board level pointer so we can modify it on the way down
604 std::string jsonPointerPathBoard = jsonPointerPath;
605 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -0800606 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -0800607 {
James Feist97a63f12018-05-17 13:50:57 -0700608 exposesIndex++;
609 jsonPointerPath = jsonPointerPathBoard;
610 jsonPointerPath += std::to_string(exposesIndex);
611
James Feistd63d18a2018-07-19 15:23:45 -0700612 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800613 if (findName == item.end())
614 {
615 std::cerr << "cannot find name in field " << item << "\n";
616 continue;
617 }
James Feist1e3e6982018-08-03 16:09:28 -0700618 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -0800619 // if status is not found it is assumed to be status = 'okay'
620 if (findStatus != item.end())
621 {
622 if (*findStatus == "disabled")
623 {
624 continue;
625 }
626 }
James Feistd63d18a2018-07-19 15:23:45 -0700627 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800628 std::string itemType;
629 if (findType != item.end())
630 {
631 itemType = findType->get<std::string>();
632 std::regex_replace(itemType.begin(), itemType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800633 itemType.end(), illegalDbusPathRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800634 }
635 else
636 {
637 itemType = "unknown";
638 }
639 std::string itemName = findName->get<std::string>();
640 std::regex_replace(itemName.begin(), itemName.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800641 itemName.end(), illegalDbusMemberRegex, "_");
Matt Spinler3d1909a2023-08-10 16:39:44 -0500642 std::string ifacePath = boardPath;
Ed Tanous07d467b2021-02-23 14:48:37 -0800643 ifacePath += "/";
644 ifacePath += itemName;
James Feistc6248a52018-08-14 10:09:45 -0700645
Sui Chen74ebe592022-09-13 10:22:03 -0700646 if (itemType == "BMC")
647 {
648 std::shared_ptr<sdbusplus::asio::dbus_interface> bmcIface =
649 createInterface(objServer, ifacePath,
650 "xyz.openbmc_project.Inventory.Item.Bmc",
Matt Spinler3d1909a2023-08-10 16:39:44 -0500651 boardNameOrig);
Sui Chen74ebe592022-09-13 10:22:03 -0700652 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
653 bmcIface, item, objServer,
654 getPermission(itemType));
655 }
Edward Leeeb587b42023-03-08 18:59:04 +0000656 else if (itemType == "System")
657 {
658 std::shared_ptr<sdbusplus::asio::dbus_interface> systemIface =
659 createInterface(objServer, ifacePath,
660 "xyz.openbmc_project.Inventory.Item.System",
Matt Spinler3d1909a2023-08-10 16:39:44 -0500661 boardNameOrig);
Edward Leeeb587b42023-03-08 18:59:04 +0000662 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
663 systemIface, item, objServer,
664 getPermission(itemType));
665 }
Sui Chen74ebe592022-09-13 10:22:03 -0700666
Patrick Williams2594d362022-09-28 06:46:24 -0500667 for (const auto& [name, config] : item.items())
James Feist1b2e2242018-01-30 13:45:19 -0800668 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030669 jsonPointerPath = jsonPointerPathBoard;
670 jsonPointerPath.append(std::to_string(exposesIndex))
671 .append("/")
672 .append(name);
673 if (config.type() == nlohmann::json::value_t::object)
James Feist1b2e2242018-01-30 13:45:19 -0800674 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030675 std::string ifaceName =
676 "xyz.openbmc_project.Configuration.";
677 ifaceName.append(itemType).append(".").append(name);
James Feist97a63f12018-05-17 13:50:57 -0700678
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030679 std::shared_ptr<sdbusplus::asio::dbus_interface>
680 objectIface = createInterface(objServer, ifacePath,
Matt Spinler3d1909a2023-08-10 16:39:44 -0500681 ifaceName, boardNameOrig);
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030682
683 populateInterfaceFromJson(
684 systemConfiguration, jsonPointerPath, objectIface,
685 config, objServer, getPermission(name));
James Feist1b2e2242018-01-30 13:45:19 -0800686 }
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030687 else if (config.type() == nlohmann::json::value_t::array)
James Feist1b2e2242018-01-30 13:45:19 -0800688 {
689 size_t index = 0;
Ed Tanous3013fb42022-07-09 08:27:06 -0700690 if (config.empty())
James Feist1b2e2242018-01-30 13:45:19 -0800691 {
James Feist8f2710a2018-05-09 17:18:55 -0700692 continue;
693 }
694 bool isLegal = true;
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030695 auto type = config[0].type();
James Feist8f2710a2018-05-09 17:18:55 -0700696 if (type != nlohmann::json::value_t::object)
697 {
698 continue;
699 }
700
701 // verify legal json
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030702 for (const auto& arrayItem : config)
James Feist8f2710a2018-05-09 17:18:55 -0700703 {
704 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -0800705 {
James Feist8f2710a2018-05-09 17:18:55 -0700706 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -0800707 break;
708 }
James Feist8f2710a2018-05-09 17:18:55 -0700709 }
710 if (!isLegal)
711 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030712 std::cerr << "dbus format error" << config << "\n";
James Feist8f2710a2018-05-09 17:18:55 -0700713 break;
714 }
715
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030716 for (auto& arrayItem : config)
James Feist8f2710a2018-05-09 17:18:55 -0700717 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030718 std::string ifaceName =
719 "xyz.openbmc_project.Configuration.";
720 ifaceName.append(itemType).append(".").append(name);
721 ifaceName.append(std::to_string(index));
James Feist97a63f12018-05-17 13:50:57 -0700722
James Feistd58879a2019-09-11 11:26:07 -0700723 std::shared_ptr<sdbusplus::asio::dbus_interface>
724 objectIface = createInterface(
Matt Spinler3d1909a2023-08-10 16:39:44 -0500725 objServer, ifacePath, ifaceName, boardNameOrig);
James Feistd58879a2019-09-11 11:26:07 -0700726
James Feistc6248a52018-08-14 10:09:45 -0700727 populateInterfaceFromJson(
728 systemConfiguration,
729 jsonPointerPath + "/" + std::to_string(index),
730 objectIface, arrayItem, objServer,
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030731 getPermission(name));
James Feistbb43d022018-06-12 15:44:33 -0700732 index++;
James Feist1b2e2242018-01-30 13:45:19 -0800733 }
734 }
735 }
Benjamin Fairca2eb042022-09-13 06:40:42 +0000736
George Liu5c1a61a2024-10-24 18:02:29 +0800737 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
738 createInterface(objServer, ifacePath,
739 "xyz.openbmc_project.Configuration." + itemType,
740 boardNameOrig);
741
742 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
743 itemIface, item, objServer,
744 getPermission(itemType));
745
Matt Spinler6eb60972023-08-14 16:36:20 -0500746 topology.addBoard(boardPath, boardType, boardNameOrig, item);
James Feist1b2e2242018-01-30 13:45:19 -0800747 }
Matt Spinler6eb60972023-08-14 16:36:20 -0500748
749 newBoards.emplace(boardPath, boardNameOrig);
James Feist1b2e2242018-01-30 13:45:19 -0800750 }
Benjamin Fairca2eb042022-09-13 06:40:42 +0000751
Matt Spinler6eb60972023-08-14 16:36:20 -0500752 for (const auto& [assocPath, assocPropValue] :
753 topology.getAssocs(newBoards))
Benjamin Fairca2eb042022-09-13 06:40:42 +0000754 {
Matt Spinler6eb60972023-08-14 16:36:20 -0500755 auto findBoard = newBoards.find(assocPath);
756 if (findBoard == newBoards.end())
757 {
758 continue;
759 }
Benjamin Fairca2eb042022-09-13 06:40:42 +0000760
Matt Spinler6eb60972023-08-14 16:36:20 -0500761 auto ifacePtr = createInterface(
762 objServer, assocPath, "xyz.openbmc_project.Association.Definitions",
763 findBoard->second);
764
765 ifacePtr->register_property("Associations", assocPropValue);
John Edward Broadbentd97c6312023-10-26 20:32:07 +0000766 tryIfaceInitialize(ifacePtr);
Benjamin Fairca2eb042022-09-13 06:40:42 +0000767 }
James Feist1b2e2242018-01-30 13:45:19 -0800768}
769
Andrew Jeffery55192932022-03-24 12:29:27 +1030770static bool deviceRequiresPowerOn(const nlohmann::json& entity)
771{
772 auto powerState = entity.find("PowerState");
Andrew Jefferyb6209442022-03-24 12:36:20 +1030773 if (powerState == entity.end())
Andrew Jeffery55192932022-03-24 12:29:27 +1030774 {
Andrew Jefferyb6209442022-03-24 12:36:20 +1030775 return false;
Andrew Jeffery55192932022-03-24 12:29:27 +1030776 }
777
Ed Tanous3013fb42022-07-09 08:27:06 -0700778 const auto* ptr = powerState->get_ptr<const std::string*>();
779 if (ptr == nullptr)
Andrew Jefferyb6209442022-03-24 12:36:20 +1030780 {
781 return false;
782 }
783
784 return *ptr == "On" || *ptr == "BiosPost";
Andrew Jeffery55192932022-03-24 12:29:27 +1030785}
786
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030787static void pruneDevice(const nlohmann::json& systemConfiguration,
788 const bool powerOff, const bool scannedPowerOff,
789 const std::string& name, const nlohmann::json& device)
790{
791 if (systemConfiguration.contains(name))
792 {
793 return;
794 }
795
Andrew Jeffery4db38bc2022-03-24 13:42:41 +1030796 if (deviceRequiresPowerOn(device) && (powerOff || scannedPowerOff))
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030797 {
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030798 return;
799 }
800
801 logDeviceRemoved(device);
802}
803
James Feistb1728ca2020-04-30 15:40:55 -0700804void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -0700805 nlohmann::json& systemConfiguration)
806{
807 static bool scannedPowerOff = false;
808 static bool scannedPowerOn = false;
809
James Feistfb00f392019-06-25 14:16:48 -0700810 if (systemConfiguration.empty() || lastJson.empty())
811 {
812 return; // not ready yet
813 }
James Feist1df06a42019-04-11 14:23:04 -0700814 if (scannedPowerOn)
815 {
816 return;
817 }
818
819 if (!isPowerOn() && scannedPowerOff)
820 {
821 return;
822 }
823
James Feistb1728ca2020-04-30 15:40:55 -0700824 timer.expires_after(std::chrono::seconds(10));
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030825 timer.async_wait(
826 [&systemConfiguration](const boost::system::error_code& ec) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400827 if (ec == boost::asio::error::operation_aborted)
828 {
829 return;
830 }
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030831
Patrick Williamsb7077432024-08-16 15:22:21 -0400832 bool powerOff = !isPowerOn();
833 for (const auto& [name, device] : lastJson.items())
834 {
835 pruneDevice(systemConfiguration, powerOff, scannedPowerOff,
836 name, device);
837 }
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030838
Patrick Williamsb7077432024-08-16 15:22:21 -0400839 scannedPowerOff = true;
840 if (!powerOff)
841 {
842 scannedPowerOn = true;
843 }
844 });
James Feist1df06a42019-04-11 14:23:04 -0700845}
846
Andrew Jeffery2f750d22022-03-24 14:32:57 +1030847static std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
848 getDeviceInterfaces(const nlohmann::json& device)
849{
850 return inventory[device["Name"].get<std::string>()];
851}
852
Andrew Jeffery0d0c1462022-03-24 15:26:39 +1030853static void pruneConfiguration(nlohmann::json& systemConfiguration,
854 sdbusplus::asio::object_server& objServer,
855 bool powerOff, const std::string& name,
856 const nlohmann::json& device)
857{
858 if (powerOff && deviceRequiresPowerOn(device))
859 {
860 // power not on yet, don't know if it's there or not
861 return;
862 }
863
864 auto& ifaces = getDeviceInterfaces(device);
865 for (auto& iface : ifaces)
866 {
867 auto sharedPtr = iface.lock();
868 if (!!sharedPtr)
869 {
870 objServer.remove_interface(sharedPtr);
871 }
872 }
873
874 ifaces.clear();
875 systemConfiguration.erase(name);
Matt Spinler6eb60972023-08-14 16:36:20 -0500876 topology.remove(device["Name"].get<std::string>());
Andrew Jeffery0d0c1462022-03-24 15:26:39 +1030877 logDeviceRemoved(device);
878}
879
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030880static void publishNewConfiguration(
881 const size_t& instance, const size_t count,
882 boost::asio::steady_timer& timer, nlohmann::json& systemConfiguration,
883 // Gerrit discussion:
884 // https://gerrit.openbmc-project.xyz/c/openbmc/entity-manager/+/52316/6
885 //
886 // Discord discussion:
887 // https://discord.com/channels/775381525260664832/867820390406422538/958048437729910854
888 //
889 // NOLINTNEXTLINE(performance-unnecessary-value-param)
890 const nlohmann::json newConfiguration,
891 sdbusplus::asio::object_server& objServer)
892{
893 loadOverlays(newConfiguration);
894
Ed Tanous49a888c2023-03-06 13:44:51 -0800895 boost::asio::post(io, [systemConfiguration]() {
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200896 if (!configuration::writeJsonFiles(systemConfiguration))
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030897 {
898 std::cerr << "Error writing json files\n";
899 }
900 });
901
Ed Tanous49a888c2023-03-06 13:44:51 -0800902 boost::asio::post(io, [&instance, count, &timer, newConfiguration,
903 &systemConfiguration, &objServer]() {
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030904 postToDbus(newConfiguration, systemConfiguration, objServer);
905 if (count == instance)
906 {
907 startRemovedTimer(timer, systemConfiguration);
908 }
909 });
910}
911
James Feist8f2710a2018-05-09 17:18:55 -0700912// main properties changed entry
James Feist4dc617b2020-05-01 09:54:47 -0700913void propertiesChangedCallback(nlohmann::json& systemConfiguration,
914 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -0700915{
James Feist2539ccd2020-05-01 16:15:08 -0700916 static bool inProgress = false;
James Feistb1728ca2020-04-30 15:40:55 -0700917 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -0700918 static size_t instance = 0;
919 instance++;
920 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -0700921
Anupama B Rbf263982024-01-25 04:42:39 -0600922 timer.expires_after(std::chrono::milliseconds(500));
James Feist8f2710a2018-05-09 17:18:55 -0700923
924 // setup an async wait as we normally get flooded with new requests
James Feist4dc617b2020-05-01 09:54:47 -0700925 timer.async_wait([&systemConfiguration, &objServer,
James Feist899e17f2019-09-13 11:46:29 -0700926 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -0700927 if (ec == boost::asio::error::operation_aborted)
928 {
929 // we were cancelled
930 return;
931 }
Ed Tanous07d467b2021-02-23 14:48:37 -0800932 if (ec)
James Feist8f2710a2018-05-09 17:18:55 -0700933 {
934 std::cerr << "async wait error " << ec << "\n";
935 return;
936 }
937
James Feist2539ccd2020-05-01 16:15:08 -0700938 if (inProgress)
939 {
940 propertiesChangedCallback(systemConfiguration, objServer);
941 return;
942 }
943 inProgress = true;
944
James Feist8f2710a2018-05-09 17:18:55 -0700945 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -0700946 auto missingConfigurations = std::make_shared<nlohmann::json>();
947 *missingConfigurations = systemConfiguration;
948
James Feist8f2710a2018-05-09 17:18:55 -0700949 std::list<nlohmann::json> configurations;
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200950 if (!configuration::loadConfigurations(configurations))
James Feist8f2710a2018-05-09 17:18:55 -0700951 {
Andrew Jefferyf3311792022-03-29 22:09:00 +1030952 std::cerr << "Could not load configurations\n";
James Feist2539ccd2020-05-01 16:15:08 -0700953 inProgress = false;
James Feist8f2710a2018-05-09 17:18:55 -0700954 return;
955 }
956
Christopher Meis26fbbd52025-03-26 14:55:06 +0100957 auto perfScan = std::make_shared<scan::PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -0700958 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -0700959 objServer,
960 [&systemConfiguration, &objServer, count, oldConfiguration,
961 missingConfigurations]() {
Patrick Williamsb7077432024-08-16 15:22:21 -0400962 // this is something that since ac has been applied to the bmc
963 // we saw, and we no longer see it
964 bool powerOff = !isPowerOn();
965 for (const auto& [name, device] :
966 missingConfigurations->items())
967 {
968 pruneConfiguration(systemConfiguration, objServer, powerOff,
969 name, device);
970 }
James Feist899e17f2019-09-13 11:46:29 -0700971
Patrick Williamsb7077432024-08-16 15:22:21 -0400972 nlohmann::json newConfiguration = systemConfiguration;
Andrew Jeffery7c4cbbe2022-03-24 15:27:10 +1030973
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200974 configuration::deriveNewConfiguration(oldConfiguration,
975 newConfiguration);
Andrew Jeffery7c4cbbe2022-03-24 15:27:10 +1030976
Patrick Williamsb7077432024-08-16 15:22:21 -0400977 for (const auto& [_, device] : newConfiguration.items())
978 {
979 logDeviceAdded(device);
980 }
James Feist899e17f2019-09-13 11:46:29 -0700981
Patrick Williamsb7077432024-08-16 15:22:21 -0400982 inProgress = false;
James Feist2539ccd2020-05-01 16:15:08 -0700983
Patrick Williamsb7077432024-08-16 15:22:21 -0400984 boost::asio::post(
985 io, std::bind_front(
986 publishNewConfiguration, std::ref(instance), count,
987 std::ref(timer), std::ref(systemConfiguration),
988 newConfiguration, std::ref(objServer)));
989 });
James Feist8f2710a2018-05-09 17:18:55 -0700990 perfScan->run();
991 });
James Feist75fdeeb2018-02-20 14:26:16 -0800992}
993
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600994// Check if InterfacesAdded payload contains an iface that needs probing.
Patrick Williamsb7077432024-08-16 15:22:21 -0400995static bool iaContainsProbeInterface(
996 sdbusplus::message_t& msg, const std::set<std::string>& probeInterfaces)
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600997{
998 sdbusplus::message::object_path path;
999 DBusObject interfaces;
1000 std::set<std::string> interfaceSet;
1001 std::set<std::string> intersect;
1002
1003 msg.read(path, interfaces);
1004
1005 std::for_each(interfaces.begin(), interfaces.end(),
1006 [&interfaceSet](const auto& iface) {
Patrick Williamsb7077432024-08-16 15:22:21 -04001007 interfaceSet.insert(iface.first);
1008 });
Matt Spinler8d1ac3a2023-02-02 09:48:04 -06001009
1010 std::set_intersection(interfaceSet.begin(), interfaceSet.end(),
1011 probeInterfaces.begin(), probeInterfaces.end(),
1012 std::inserter(intersect, intersect.end()));
1013 return !intersect.empty();
1014}
1015
1016// Check if InterfacesRemoved payload contains an iface that needs probing.
Patrick Williamsb7077432024-08-16 15:22:21 -04001017static bool irContainsProbeInterface(
1018 sdbusplus::message_t& msg, const std::set<std::string>& probeInterfaces)
Matt Spinler8d1ac3a2023-02-02 09:48:04 -06001019{
1020 sdbusplus::message::object_path path;
1021 std::set<std::string> interfaces;
1022 std::set<std::string> intersect;
1023
1024 msg.read(path, interfaces);
1025
1026 std::set_intersection(interfaces.begin(), interfaces.end(),
1027 probeInterfaces.begin(), probeInterfaces.end(),
1028 std::inserter(intersect, intersect.end()));
1029 return !intersect.empty();
1030}
1031
James Feist98132792019-07-09 13:29:09 -07001032int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001033{
1034 // setup connection to dbus
Ed Tanous07d467b2021-02-23 14:48:37 -08001035 systemBus = std::make_shared<sdbusplus::asio::connection>(io);
1036 systemBus->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001037
Nan Zhoua3315672022-09-20 19:48:14 +00001038 // The EntityManager object itself doesn't expose any properties.
1039 // No need to set up ObjectManager for the |EntityManager| object.
1040 sdbusplus::asio::object_server objServer(systemBus, /*skipManager=*/true);
1041
1042 // All other objects that EntityManager currently support are under the
1043 // inventory subtree.
1044 // See the discussion at
1045 // https://discord.com/channels/775381525260664832/1018929092009144380
1046 objServer.add_manager("/xyz/openbmc_project/inventory");
James Feistfd1264a2018-05-03 12:10:00 -07001047
James Feist8f2710a2018-05-09 17:18:55 -07001048 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1049 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1050 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001051
James Feist4131aea2018-03-09 09:47:30 -08001052 // to keep reference to the match / filter objects so they don't get
1053 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001054
1055 nlohmann::json systemConfiguration = nlohmann::json::object();
1056
Christopher Meisbdaa6b22025-04-02 10:49:02 +02001057 std::set<std::string> probeInterfaces = configuration::getProbeInterfaces();
Matt Spinler8d1ac3a2023-02-02 09:48:04 -06001058
Brad Bishopc76af0f2020-12-04 13:50:23 -05001059 // We need a poke from DBus for static providers that create all their
1060 // objects prior to claiming a well-known name, and thus don't emit any
1061 // org.freedesktop.DBus.Properties signals. Similarly if a process exits
1062 // for any reason, expected or otherwise, we'll need a poke to remove
1063 // entities from DBus.
Patrick Williams2af39222022-07-22 19:26:56 -05001064 sdbusplus::bus::match_t nameOwnerChangedMatch(
1065 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishopc76af0f2020-12-04 13:50:23 -05001066 sdbusplus::bus::match::rules::nameOwnerChanged(),
Patrick Williams7b8786f2022-10-10 10:23:37 -05001067 [&](sdbusplus::message_t& m) {
Patrick Williamsb7077432024-08-16 15:22:21 -04001068 auto [name, oldOwner,
1069 newOwner] = m.unpack<std::string, std::string, std::string>();
Patrick Williams7b8786f2022-10-10 10:23:37 -05001070
Patrick Williamsb7077432024-08-16 15:22:21 -04001071 if (name.starts_with(':'))
1072 {
1073 // We should do nothing with unique-name connections.
1074 return;
1075 }
Patrick Williams7b8786f2022-10-10 10:23:37 -05001076
Patrick Williamsb7077432024-08-16 15:22:21 -04001077 propertiesChangedCallback(systemConfiguration, objServer);
1078 });
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001079 // We also need a poke from DBus when new interfaces are created or
1080 // destroyed.
Patrick Williams2af39222022-07-22 19:26:56 -05001081 sdbusplus::bus::match_t interfacesAddedMatch(
1082 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001083 sdbusplus::bus::match::rules::interfacesAdded(),
Matt Spinler8d1ac3a2023-02-02 09:48:04 -06001084 [&](sdbusplus::message_t& msg) {
Patrick Williamsb7077432024-08-16 15:22:21 -04001085 if (iaContainsProbeInterface(msg, probeInterfaces))
1086 {
1087 propertiesChangedCallback(systemConfiguration, objServer);
1088 }
1089 });
Patrick Williams2af39222022-07-22 19:26:56 -05001090 sdbusplus::bus::match_t interfacesRemovedMatch(
1091 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001092 sdbusplus::bus::match::rules::interfacesRemoved(),
Matt Spinler8d1ac3a2023-02-02 09:48:04 -06001093 [&](sdbusplus::message_t& msg) {
Patrick Williamsb7077432024-08-16 15:22:21 -04001094 if (irContainsProbeInterface(msg, probeInterfaces))
1095 {
1096 propertiesChangedCallback(systemConfiguration, objServer);
1097 }
1098 });
Brad Bishopc76af0f2020-12-04 13:50:23 -05001099
Ed Tanous49a888c2023-03-06 13:44:51 -08001100 boost::asio::post(io, [&]() {
1101 propertiesChangedCallback(systemConfiguration, objServer);
1102 });
James Feist4131aea2018-03-09 09:47:30 -08001103
James Feistfd1264a2018-05-03 12:10:00 -07001104 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -07001105 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001106 });
John Edward Broadbentd97c6312023-10-26 20:32:07 +00001107 tryIfaceInitialize(entityIface);
James Feist8f2710a2018-05-09 17:18:55 -07001108
James Feist1df06a42019-04-11 14:23:04 -07001109 if (fwVersionIsSame())
1110 {
Christopher Meisbdaa6b22025-04-02 10:49:02 +02001111 if (std::filesystem::is_regular_file(
1112 configuration::currentConfiguration))
James Feist1df06a42019-04-11 14:23:04 -07001113 {
1114 // this file could just be deleted, but it's nice for debug
1115 std::filesystem::create_directory(tempConfigDir);
1116 std::filesystem::remove(lastConfiguration);
Christopher Meisbdaa6b22025-04-02 10:49:02 +02001117 std::filesystem::copy(configuration::currentConfiguration,
1118 lastConfiguration);
1119 std::filesystem::remove(configuration::currentConfiguration);
James Feist1df06a42019-04-11 14:23:04 -07001120
1121 std::ifstream jsonStream(lastConfiguration);
1122 if (jsonStream.good())
1123 {
1124 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1125 if (data.is_discarded())
1126 {
Patrick Williamsb7077432024-08-16 15:22:21 -04001127 std::cerr
1128 << "syntax error in " << lastConfiguration << "\n";
James Feist1df06a42019-04-11 14:23:04 -07001129 }
1130 else
1131 {
1132 lastJson = std::move(data);
1133 }
1134 }
1135 else
1136 {
1137 std::cerr << "unable to open " << lastConfiguration << "\n";
1138 }
1139 }
1140 }
1141 else
1142 {
1143 // not an error, just logging at this level to make it in the journal
1144 std::cerr << "Clearing previous configuration\n";
Christopher Meisbdaa6b22025-04-02 10:49:02 +02001145 std::filesystem::remove(configuration::currentConfiguration);
James Feist1df06a42019-04-11 14:23:04 -07001146 }
1147
1148 // some boards only show up after power is on, we want to not say they are
1149 // removed until the same state happens
Ed Tanous07d467b2021-02-23 14:48:37 -08001150 setupPowerMatch(systemBus);
James Feist1df06a42019-04-11 14:23:04 -07001151
James Feist1b2e2242018-01-30 13:45:19 -08001152 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001153
1154 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001155}