blob: 522c6f3e0a7bb8eec869618b2366a56e810fbf4d [file] [log] [blame]
Alexander Hansen4e1142d2025-07-25 17:07:27 +02001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
James Feist3cb5fec2018-01-23 14:41:51 -08003
Brad Bishope45d8c72022-05-25 15:12:53 -04004#include "entity_manager.hpp"
James Feist1df06a42019-04-11 14:23:04 -07005
Christopher Meisfc9e7fd2025-04-03 13:13:35 +02006#include "../utils.hpp"
7#include "../variant_visitors.hpp"
Christopher Meisbdaa6b22025-04-02 10:49:02 +02008#include "configuration.hpp"
Christopher Meis12bea9b2025-04-03 10:14:42 +02009#include "dbus_interface.hpp"
Alexander Hansenf57a2592025-06-27 15:07:07 +020010#include "log_device_inventory.hpp"
Brad Bishope45d8c72022-05-25 15:12:53 -040011#include "overlay.hpp"
Christopher Meis26fbbd52025-03-26 14:55:06 +010012#include "perform_scan.hpp"
Benjamin Fairca2eb042022-09-13 06:40:42 +000013#include "topology.hpp"
Christopher Meis59ef1e72025-04-16 08:53:25 +020014#include "utils.hpp"
James Feist481c5d52019-08-13 14:40:40 -070015
James Feist11be6672018-04-06 14:05:32 -070016#include <boost/algorithm/string/case_conv.hpp>
James Feist02d2b932020-02-06 16:28:48 -080017#include <boost/asio/io_context.hpp>
Ed Tanous49a888c2023-03-06 13:44:51 -080018#include <boost/asio/post.hpp>
James Feistb1728ca2020-04-30 15:40:55 -070019#include <boost/asio/steady_timer.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080020#include <boost/container/flat_map.hpp>
21#include <boost/container/flat_set.hpp>
James Feistf5125b02019-06-06 11:27:43 -070022#include <boost/range/iterator_range.hpp>
James Feist8c505da2020-05-28 10:06:33 -070023#include <nlohmann/json.hpp>
Alexander Hansen8feb0452025-09-15 14:29:20 +020024#include <phosphor-logging/lg2.hpp>
James Feist8c505da2020-05-28 10:06:33 -070025#include <sdbusplus/asio/connection.hpp>
26#include <sdbusplus/asio/object_server.hpp>
27
James Feist637b3ef2019-04-15 16:35:30 -070028#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080029#include <fstream>
Andrew Jefferye35d0ac2022-03-24 15:53:13 +103030#include <functional>
Andrew Jeffery666583b2021-12-01 15:50:12 +103031#include <map>
James Feista465ccc2019-02-08 12:51:01 -080032#include <regex>
James Feist1df06a42019-04-11 14:23:04 -070033constexpr const char* tempConfigDir = "/tmp/configuration/";
34constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
James Feistf1b14142019-04-10 15:22:09 -070035
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +020036static constexpr std::array<const char*, 6> settableInterfaces = {
37 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
James Feist3cb5fec2018-01-23 14:41:51 -080038
Ed Tanous07d467b2021-02-23 14:48:37 -080039const std::regex illegalDbusPathRegex("[^A-Za-z0-9_.]");
40const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -080041
James Feista465ccc2019-02-08 12:51:01 -080042sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -070043{
44 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
45 interface) != settableInterfaces.end()
46 ? sdbusplus::asio::PropertyPermission::readWrite
47 : sdbusplus::asio::PropertyPermission::readOnly;
48}
49
Christopher Meiscf6a75b2025-06-03 07:53:50 +020050EntityManager::EntityManager(
Alexander Hansena555acf2025-06-27 11:59:10 +020051 std::shared_ptr<sdbusplus::asio::connection>& systemBus,
52 boost::asio::io_context& io) :
Christopher Meiscf6a75b2025-06-03 07:53:50 +020053 systemBus(systemBus),
54 objServer(sdbusplus::asio::object_server(systemBus, /*skipManager=*/true)),
55 lastJson(nlohmann::json::object()),
Alexander Hansenb1340da2025-06-27 14:29:13 +020056 systemConfiguration(nlohmann::json::object()), io(io),
Alexander Hansen89737252025-08-04 15:15:13 +020057 dbus_interface(io, objServer), powerStatus(*systemBus),
58 propertiesChangedTimer(io)
Christopher Meiscf6a75b2025-06-03 07:53:50 +020059{
60 // All other objects that EntityManager currently support are under the
61 // inventory subtree.
62 // See the discussion at
63 // https://discord.com/channels/775381525260664832/1018929092009144380
64 objServer.add_manager("/xyz/openbmc_project/inventory");
James Feist75fdeeb2018-02-20 14:26:16 -080065
Christopher Meiscf6a75b2025-06-03 07:53:50 +020066 entityIface = objServer.add_interface("/xyz/openbmc_project/EntityManager",
67 "xyz.openbmc_project.EntityManager");
68 entityIface->register_method("ReScan", [this]() {
69 propertiesChangedCallback();
70 });
71 dbus_interface::tryIfaceInitialize(entityIface);
Christopher Meisf7252572025-06-11 13:22:05 +020072
73 initFilters(configuration.probeInterfaces);
Christopher Meiscf6a75b2025-06-03 07:53:50 +020074}
75
76void EntityManager::postToDbus(const nlohmann::json& newConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -080077{
Matt Spinler6eb60972023-08-14 16:36:20 -050078 std::map<std::string, std::string> newBoards; // path -> name
Benjamin Fairca2eb042022-09-13 06:40:42 +000079
James Feist97a63f12018-05-17 13:50:57 -070080 // iterate through boards
Patrick Williams2594d362022-09-28 06:46:24 -050081 for (const auto& [boardId, boardConfig] : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -080082 {
Alexander Hansen4fcd9462025-07-30 17:44:44 +020083 postBoardToDBus(boardId, boardConfig, newBoards);
James Feist1b2e2242018-01-30 13:45:19 -080084 }
Benjamin Fairca2eb042022-09-13 06:40:42 +000085
Matt Spinler6eb60972023-08-14 16:36:20 -050086 for (const auto& [assocPath, assocPropValue] :
Alexander Hansen32e74182025-08-20 16:16:00 +020087 topology.getAssocs(std::views::keys(newBoards)))
Benjamin Fairca2eb042022-09-13 06:40:42 +000088 {
Matt Spinler6eb60972023-08-14 16:36:20 -050089 auto findBoard = newBoards.find(assocPath);
90 if (findBoard == newBoards.end())
91 {
92 continue;
93 }
Benjamin Fairca2eb042022-09-13 06:40:42 +000094
Alexander Hansen57604ed2025-06-27 13:22:28 +020095 auto ifacePtr = dbus_interface.createInterface(
Alexander Hansen89737252025-08-04 15:15:13 +020096 assocPath, "xyz.openbmc_project.Association.Definitions",
Matt Spinler6eb60972023-08-14 16:36:20 -050097 findBoard->second);
98
99 ifacePtr->register_property("Associations", assocPropValue);
Christopher Meis12bea9b2025-04-03 10:14:42 +0200100 dbus_interface::tryIfaceInitialize(ifacePtr);
Benjamin Fairca2eb042022-09-13 06:40:42 +0000101 }
James Feist1b2e2242018-01-30 13:45:19 -0800102}
103
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200104void EntityManager::postBoardToDBus(
105 const std::string& boardId, const nlohmann::json& boardConfig,
106 std::map<std::string, std::string>& newBoards)
107{
108 std::string boardName = boardConfig["Name"];
109 std::string boardNameOrig = boardConfig["Name"];
110 std::string jsonPointerPath = "/" + boardId;
111 // loop through newConfiguration, but use values from system
112 // configuration to be able to modify via dbus later
113 auto boardValues = systemConfiguration[boardId];
114 auto findBoardType = boardValues.find("Type");
115 std::string boardType;
116 if (findBoardType != boardValues.end() &&
117 findBoardType->type() == nlohmann::json::value_t::string)
118 {
119 boardType = findBoardType->get<std::string>();
120 std::regex_replace(boardType.begin(), boardType.begin(),
121 boardType.end(), illegalDbusMemberRegex, "_");
122 }
123 else
124 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200125 lg2::error("Unable to find type for {BOARD} reverting to Chassis.",
126 "BOARD", boardName);
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200127 boardType = "Chassis";
128 }
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200129
Christopher Meis811160e2025-08-08 08:48:37 +0200130 const std::string boardPath =
131 em_utils::buildInventorySystemPath(boardName, boardType);
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200132
133 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
Alexander Hansen89737252025-08-04 15:15:13 +0200134 dbus_interface.createInterface(
135 boardPath, "xyz.openbmc_project.Inventory.Item", boardName);
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200136
137 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
138 dbus_interface.createInterface(
Alexander Hansen89737252025-08-04 15:15:13 +0200139 boardPath, "xyz.openbmc_project.Inventory.Item." + boardType,
140 boardNameOrig);
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200141
Alexander Hansen89737252025-08-04 15:15:13 +0200142 dbus_interface.createAddObjectMethod(jsonPointerPath, boardPath,
143 systemConfiguration, boardNameOrig);
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200144
Alexander Hansen89737252025-08-04 15:15:13 +0200145 dbus_interface.populateInterfaceFromJson(
146 systemConfiguration, jsonPointerPath, boardIface, boardValues);
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200147 jsonPointerPath += "/";
148 // iterate through board properties
149 for (const auto& [propName, propValue] : boardValues.items())
150 {
151 if (propValue.type() == nlohmann::json::value_t::object)
152 {
153 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
Alexander Hansen89737252025-08-04 15:15:13 +0200154 dbus_interface.createInterface(boardPath, propName,
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200155 boardNameOrig);
156
Alexander Hansen89737252025-08-04 15:15:13 +0200157 dbus_interface.populateInterfaceFromJson(
158 systemConfiguration, jsonPointerPath + propName, iface,
159 propValue);
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200160 }
161 }
162
Alexander Hansen4fa40122025-07-30 18:26:35 +0200163 nlohmann::json::iterator exposes = boardValues.find("Exposes");
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200164 if (exposes == boardValues.end())
165 {
166 return;
167 }
168 // iterate through exposes
169 jsonPointerPath += "Exposes/";
170
171 // store the board level pointer so we can modify it on the way down
172 std::string jsonPointerPathBoard = jsonPointerPath;
173 size_t exposesIndex = -1;
Alexander Hansen4fa40122025-07-30 18:26:35 +0200174 for (nlohmann::json& item : *exposes)
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200175 {
Alexander Hansen4fa40122025-07-30 18:26:35 +0200176 postExposesRecordsToDBus(item, exposesIndex, boardNameOrig,
177 jsonPointerPath, jsonPointerPathBoard,
178 boardPath, boardType);
179 }
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200180
Alexander Hansen4fa40122025-07-30 18:26:35 +0200181 newBoards.emplace(boardPath, boardNameOrig);
182}
183
184void EntityManager::postExposesRecordsToDBus(
185 nlohmann::json& item, size_t& exposesIndex,
186 const std::string& boardNameOrig, std::string jsonPointerPath,
187 const std::string& jsonPointerPathBoard, const std::string& boardPath,
188 const std::string& boardType)
189{
190 exposesIndex++;
191 jsonPointerPath = jsonPointerPathBoard;
192 jsonPointerPath += std::to_string(exposesIndex);
193
194 auto findName = item.find("Name");
195 if (findName == item.end())
196 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200197 lg2::error("cannot find name in field {ITEM}", "ITEM", item);
Alexander Hansen4fa40122025-07-30 18:26:35 +0200198 return;
199 }
200 auto findStatus = item.find("Status");
201 // if status is not found it is assumed to be status = 'okay'
202 if (findStatus != item.end())
203 {
204 if (*findStatus == "disabled")
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200205 {
Alexander Hansen4fa40122025-07-30 18:26:35 +0200206 return;
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200207 }
Alexander Hansen4fa40122025-07-30 18:26:35 +0200208 }
209 auto findType = item.find("Type");
210 std::string itemType;
211 if (findType != item.end())
212 {
213 itemType = findType->get<std::string>();
214 std::regex_replace(itemType.begin(), itemType.begin(), itemType.end(),
215 illegalDbusPathRegex, "_");
216 }
217 else
218 {
219 itemType = "unknown";
220 }
221 std::string itemName = findName->get<std::string>();
222 std::regex_replace(itemName.begin(), itemName.begin(), itemName.end(),
223 illegalDbusMemberRegex, "_");
224 std::string ifacePath = boardPath;
225 ifacePath += "/";
226 ifacePath += itemName;
227
228 if (itemType == "BMC")
229 {
230 std::shared_ptr<sdbusplus::asio::dbus_interface> bmcIface =
231 dbus_interface.createInterface(
Alexander Hansen89737252025-08-04 15:15:13 +0200232 ifacePath, "xyz.openbmc_project.Inventory.Item.Bmc",
Alexander Hansen4fa40122025-07-30 18:26:35 +0200233 boardNameOrig);
Alexander Hansen89737252025-08-04 15:15:13 +0200234 dbus_interface.populateInterfaceFromJson(
235 systemConfiguration, jsonPointerPath, bmcIface, item,
Alexander Hansen4fa40122025-07-30 18:26:35 +0200236 getPermission(itemType));
237 }
238 else if (itemType == "System")
239 {
240 std::shared_ptr<sdbusplus::asio::dbus_interface> systemIface =
241 dbus_interface.createInterface(
Alexander Hansen89737252025-08-04 15:15:13 +0200242 ifacePath, "xyz.openbmc_project.Inventory.Item.System",
243 boardNameOrig);
244 dbus_interface.populateInterfaceFromJson(
245 systemConfiguration, jsonPointerPath, systemIface, item,
246 getPermission(itemType));
Alexander Hansen4fa40122025-07-30 18:26:35 +0200247 }
248
249 for (const auto& [name, config] : item.items())
250 {
251 jsonPointerPath = jsonPointerPathBoard;
252 jsonPointerPath.append(std::to_string(exposesIndex))
253 .append("/")
254 .append(name);
Alexander Hansen5aa65d82025-07-31 14:08:42 +0200255
256 if (!postConfigurationRecord(name, config, boardNameOrig, itemType,
257 jsonPointerPath, ifacePath))
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200258 {
Alexander Hansen5aa65d82025-07-31 14:08:42 +0200259 break;
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200260 }
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200261 }
262
Alexander Hansen4fa40122025-07-30 18:26:35 +0200263 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
264 dbus_interface.createInterface(
Alexander Hansen89737252025-08-04 15:15:13 +0200265 ifacePath, "xyz.openbmc_project.Configuration." + itemType,
266 boardNameOrig);
Alexander Hansen4fa40122025-07-30 18:26:35 +0200267
Alexander Hansen89737252025-08-04 15:15:13 +0200268 dbus_interface.populateInterfaceFromJson(
269 systemConfiguration, jsonPointerPath, itemIface, item,
Alexander Hansen4fa40122025-07-30 18:26:35 +0200270 getPermission(itemType));
271
272 topology.addBoard(boardPath, boardType, boardNameOrig, item);
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200273}
274
Alexander Hansen5aa65d82025-07-31 14:08:42 +0200275bool EntityManager::postConfigurationRecord(
276 const std::string& name, nlohmann::json& config,
277 const std::string& boardNameOrig, const std::string& itemType,
278 const std::string& jsonPointerPath, const std::string& ifacePath)
279{
280 if (config.type() == nlohmann::json::value_t::object)
281 {
282 std::string ifaceName = "xyz.openbmc_project.Configuration.";
283 ifaceName.append(itemType).append(".").append(name);
284
285 std::shared_ptr<sdbusplus::asio::dbus_interface> objectIface =
Alexander Hansen89737252025-08-04 15:15:13 +0200286 dbus_interface.createInterface(ifacePath, ifaceName, boardNameOrig);
Alexander Hansen5aa65d82025-07-31 14:08:42 +0200287
Alexander Hansen89737252025-08-04 15:15:13 +0200288 dbus_interface.populateInterfaceFromJson(
289 systemConfiguration, jsonPointerPath, objectIface, config,
290 getPermission(name));
Alexander Hansen5aa65d82025-07-31 14:08:42 +0200291 }
292 else if (config.type() == nlohmann::json::value_t::array)
293 {
294 size_t index = 0;
295 if (config.empty())
296 {
297 return true;
298 }
299 bool isLegal = true;
300 auto type = config[0].type();
301 if (type != nlohmann::json::value_t::object)
302 {
303 return true;
304 }
305
306 // verify legal json
307 for (const auto& arrayItem : config)
308 {
309 if (arrayItem.type() != type)
310 {
311 isLegal = false;
312 break;
313 }
314 }
315 if (!isLegal)
316 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200317 lg2::error("dbus format error {JSON}", "JSON", config);
Alexander Hansen5aa65d82025-07-31 14:08:42 +0200318 return false;
319 }
320
321 for (auto& arrayItem : config)
322 {
323 std::string ifaceName = "xyz.openbmc_project.Configuration.";
324 ifaceName.append(itemType).append(".").append(name);
325 ifaceName.append(std::to_string(index));
326
327 std::shared_ptr<sdbusplus::asio::dbus_interface> objectIface =
Alexander Hansen89737252025-08-04 15:15:13 +0200328 dbus_interface.createInterface(ifacePath, ifaceName,
Alexander Hansen5aa65d82025-07-31 14:08:42 +0200329 boardNameOrig);
330
Alexander Hansen89737252025-08-04 15:15:13 +0200331 dbus_interface.populateInterfaceFromJson(
332 systemConfiguration,
Alexander Hansen5aa65d82025-07-31 14:08:42 +0200333 jsonPointerPath + "/" + std::to_string(index), objectIface,
Alexander Hansen89737252025-08-04 15:15:13 +0200334 arrayItem, getPermission(name));
Alexander Hansen5aa65d82025-07-31 14:08:42 +0200335 index++;
336 }
337 }
338
339 return true;
340}
341
Andrew Jeffery55192932022-03-24 12:29:27 +1030342static bool deviceRequiresPowerOn(const nlohmann::json& entity)
343{
344 auto powerState = entity.find("PowerState");
Andrew Jefferyb6209442022-03-24 12:36:20 +1030345 if (powerState == entity.end())
Andrew Jeffery55192932022-03-24 12:29:27 +1030346 {
Andrew Jefferyb6209442022-03-24 12:36:20 +1030347 return false;
Andrew Jeffery55192932022-03-24 12:29:27 +1030348 }
349
Ed Tanous3013fb42022-07-09 08:27:06 -0700350 const auto* ptr = powerState->get_ptr<const std::string*>();
351 if (ptr == nullptr)
Andrew Jefferyb6209442022-03-24 12:36:20 +1030352 {
353 return false;
354 }
355
356 return *ptr == "On" || *ptr == "BiosPost";
Andrew Jeffery55192932022-03-24 12:29:27 +1030357}
358
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030359static void pruneDevice(const nlohmann::json& systemConfiguration,
360 const bool powerOff, const bool scannedPowerOff,
361 const std::string& name, const nlohmann::json& device)
362{
363 if (systemConfiguration.contains(name))
364 {
365 return;
366 }
367
Andrew Jeffery4db38bc2022-03-24 13:42:41 +1030368 if (deviceRequiresPowerOn(device) && (powerOff || scannedPowerOff))
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030369 {
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030370 return;
371 }
372
373 logDeviceRemoved(device);
374}
375
Alexander Hansen95ab18f2025-06-27 13:58:10 +0200376void EntityManager::startRemovedTimer(boost::asio::steady_timer& timer,
377 nlohmann::json& systemConfiguration)
James Feist1df06a42019-04-11 14:23:04 -0700378{
James Feistfb00f392019-06-25 14:16:48 -0700379 if (systemConfiguration.empty() || lastJson.empty())
380 {
381 return; // not ready yet
382 }
James Feist1df06a42019-04-11 14:23:04 -0700383 if (scannedPowerOn)
384 {
385 return;
386 }
387
Alexander Hansen0ab32b32025-06-27 14:50:33 +0200388 if (!powerStatus.isPowerOn() && scannedPowerOff)
James Feist1df06a42019-04-11 14:23:04 -0700389 {
390 return;
391 }
392
James Feistb1728ca2020-04-30 15:40:55 -0700393 timer.expires_after(std::chrono::seconds(10));
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030394 timer.async_wait(
Alexander Hansen95ab18f2025-06-27 13:58:10 +0200395 [&systemConfiguration, this](const boost::system::error_code& ec) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400396 if (ec == boost::asio::error::operation_aborted)
397 {
398 return;
399 }
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030400
Alexander Hansen0ab32b32025-06-27 14:50:33 +0200401 bool powerOff = !powerStatus.isPowerOn();
Patrick Williamsb7077432024-08-16 15:22:21 -0400402 for (const auto& [name, device] : lastJson.items())
403 {
404 pruneDevice(systemConfiguration, powerOff, scannedPowerOff,
405 name, device);
406 }
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030407
Patrick Williamsb7077432024-08-16 15:22:21 -0400408 scannedPowerOff = true;
409 if (!powerOff)
410 {
411 scannedPowerOn = true;
412 }
413 });
James Feist1df06a42019-04-11 14:23:04 -0700414}
415
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200416void EntityManager::pruneConfiguration(bool powerOff, const std::string& name,
417 const nlohmann::json& device)
Andrew Jeffery0d0c1462022-03-24 15:26:39 +1030418{
419 if (powerOff && deviceRequiresPowerOn(device))
420 {
421 // power not on yet, don't know if it's there or not
422 return;
423 }
424
Alexander Hansen57604ed2025-06-27 13:22:28 +0200425 auto& ifaces = dbus_interface.getDeviceInterfaces(device);
Andrew Jeffery0d0c1462022-03-24 15:26:39 +1030426 for (auto& iface : ifaces)
427 {
428 auto sharedPtr = iface.lock();
429 if (!!sharedPtr)
430 {
431 objServer.remove_interface(sharedPtr);
432 }
433 }
434
435 ifaces.clear();
436 systemConfiguration.erase(name);
Matt Spinler6eb60972023-08-14 16:36:20 -0500437 topology.remove(device["Name"].get<std::string>());
Andrew Jeffery0d0c1462022-03-24 15:26:39 +1030438 logDeviceRemoved(device);
439}
440
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200441void EntityManager::publishNewConfiguration(
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030442 const size_t& instance, const size_t count,
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200443 boost::asio::steady_timer& timer, // Gerrit discussion:
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030444 // https://gerrit.openbmc-project.xyz/c/openbmc/entity-manager/+/52316/6
445 //
446 // Discord discussion:
447 // https://discord.com/channels/775381525260664832/867820390406422538/958048437729910854
448 //
449 // NOLINTNEXTLINE(performance-unnecessary-value-param)
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200450 const nlohmann::json newConfiguration)
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030451{
Alexander Hansena555acf2025-06-27 11:59:10 +0200452 loadOverlays(newConfiguration, io);
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030453
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200454 boost::asio::post(io, [this]() {
Christopher Meisf7252572025-06-11 13:22:05 +0200455 if (!writeJsonFiles(systemConfiguration))
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030456 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200457 lg2::error("Error writing json files");
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030458 }
459 });
460
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200461 boost::asio::post(io, [this, &instance, count, &timer, newConfiguration]() {
462 postToDbus(newConfiguration);
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030463 if (count == instance)
464 {
Alexander Hansen95ab18f2025-06-27 13:58:10 +0200465 startRemovedTimer(timer, systemConfiguration);
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030466 }
467 });
468}
469
James Feist8f2710a2018-05-09 17:18:55 -0700470// main properties changed entry
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200471void EntityManager::propertiesChangedCallback()
James Feist8f2710a2018-05-09 17:18:55 -0700472{
Alexander Hansenfc1b1e22025-06-27 14:34:11 +0200473 propertiesChangedInstance++;
474 size_t count = propertiesChangedInstance;
James Feist1df06a42019-04-11 14:23:04 -0700475
Alexander Hansenb1340da2025-06-27 14:29:13 +0200476 propertiesChangedTimer.expires_after(std::chrono::milliseconds(500));
James Feist8f2710a2018-05-09 17:18:55 -0700477
478 // setup an async wait as we normally get flooded with new requests
Alexander Hansenb1340da2025-06-27 14:29:13 +0200479 propertiesChangedTimer.async_wait(
480 [this, count](const boost::system::error_code& ec) {
481 if (ec == boost::asio::error::operation_aborted)
482 {
483 // we were cancelled
484 return;
485 }
486 if (ec)
487 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200488 lg2::error("async wait error {ERR}", "ERR", ec.message());
Alexander Hansenb1340da2025-06-27 14:29:13 +0200489 return;
490 }
James Feist8f2710a2018-05-09 17:18:55 -0700491
Alexander Hansenb1340da2025-06-27 14:29:13 +0200492 if (propertiesChangedInProgress)
493 {
494 propertiesChangedCallback();
495 return;
496 }
497 propertiesChangedInProgress = true;
James Feist2539ccd2020-05-01 16:15:08 -0700498
Alexander Hansenb1340da2025-06-27 14:29:13 +0200499 nlohmann::json oldConfiguration = systemConfiguration;
500 auto missingConfigurations = std::make_shared<nlohmann::json>();
501 *missingConfigurations = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -0700502
Alexander Hansenb1340da2025-06-27 14:29:13 +0200503 auto perfScan = std::make_shared<scan::PerformScan>(
Christopher Meisf7252572025-06-11 13:22:05 +0200504 *this, *missingConfigurations, configuration.configurations, io,
Alexander Hansenb1340da2025-06-27 14:29:13 +0200505 [this, count, oldConfiguration, missingConfigurations]() {
506 // this is something that since ac has been applied to the
507 // bmc we saw, and we no longer see it
Alexander Hansen0ab32b32025-06-27 14:50:33 +0200508 bool powerOff = !powerStatus.isPowerOn();
Alexander Hansenb1340da2025-06-27 14:29:13 +0200509 for (const auto& [name, device] :
510 missingConfigurations->items())
511 {
512 pruneConfiguration(powerOff, name, device);
513 }
Alexander Hansenb1340da2025-06-27 14:29:13 +0200514 nlohmann::json newConfiguration = systemConfiguration;
515
Christopher Meisf7252572025-06-11 13:22:05 +0200516 deriveNewConfiguration(oldConfiguration, newConfiguration);
Alexander Hansenb1340da2025-06-27 14:29:13 +0200517
518 for (const auto& [_, device] : newConfiguration.items())
519 {
520 logDeviceAdded(device);
521 }
522
523 propertiesChangedInProgress = false;
524
525 boost::asio::post(io, [this, newConfiguration, count] {
526 publishNewConfiguration(
Alexander Hansenfc1b1e22025-06-27 14:34:11 +0200527 std::ref(propertiesChangedInstance), count,
Alexander Hansenb1340da2025-06-27 14:29:13 +0200528 std::ref(propertiesChangedTimer), newConfiguration);
529 });
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200530 });
Alexander Hansenb1340da2025-06-27 14:29:13 +0200531 perfScan->run();
532 });
James Feist75fdeeb2018-02-20 14:26:16 -0800533}
534
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600535// Check if InterfacesAdded payload contains an iface that needs probing.
Patrick Williamsb7077432024-08-16 15:22:21 -0400536static bool iaContainsProbeInterface(
Christopher Meisf7252572025-06-11 13:22:05 +0200537 sdbusplus::message_t& msg,
538 const std::unordered_set<std::string>& probeInterfaces)
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600539{
540 sdbusplus::message::object_path path;
541 DBusObject interfaces;
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600542 msg.read(path, interfaces);
Eldin Lee802cbf22025-08-22 14:44:04 +0800543 return std::ranges::any_of(interfaces | std::views::keys,
544 [&probeInterfaces](const auto& ifaceName) {
545 return probeInterfaces.contains(ifaceName);
546 });
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600547}
548
549// Check if InterfacesRemoved payload contains an iface that needs probing.
Patrick Williamsb7077432024-08-16 15:22:21 -0400550static bool irContainsProbeInterface(
Christopher Meisf7252572025-06-11 13:22:05 +0200551 sdbusplus::message_t& msg,
552 const std::unordered_set<std::string>& probeInterfaces)
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600553{
554 sdbusplus::message::object_path path;
Eldin Lee802cbf22025-08-22 14:44:04 +0800555 std::vector<std::string> interfaces;
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600556 msg.read(path, interfaces);
Eldin Lee802cbf22025-08-22 14:44:04 +0800557 return std::ranges::any_of(interfaces,
558 [&probeInterfaces](const auto& ifaceName) {
559 return probeInterfaces.contains(ifaceName);
560 });
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600561}
562
Alexander Hansenad28e402025-06-25 12:38:20 +0200563void EntityManager::handleCurrentConfigurationJson()
564{
Alexander Hansene6651852025-01-21 16:22:05 +0100565 if (EM_CACHE_CONFIGURATION && em_utils::fwVersionIsSame())
Alexander Hansenad28e402025-06-25 12:38:20 +0200566 {
Christopher Meisf7252572025-06-11 13:22:05 +0200567 if (std::filesystem::is_regular_file(currentConfiguration))
Alexander Hansenad28e402025-06-25 12:38:20 +0200568 {
569 // this file could just be deleted, but it's nice for debug
570 std::filesystem::create_directory(tempConfigDir);
571 std::filesystem::remove(lastConfiguration);
Christopher Meisf7252572025-06-11 13:22:05 +0200572 std::filesystem::copy(currentConfiguration, lastConfiguration);
573 std::filesystem::remove(currentConfiguration);
Alexander Hansenad28e402025-06-25 12:38:20 +0200574
575 std::ifstream jsonStream(lastConfiguration);
576 if (jsonStream.good())
577 {
578 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
579 if (data.is_discarded())
580 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200581 lg2::error("syntax error in {PATH}", "PATH",
582 lastConfiguration);
Alexander Hansenad28e402025-06-25 12:38:20 +0200583 }
584 else
585 {
586 lastJson = std::move(data);
587 }
588 }
589 else
590 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200591 lg2::error("unable to open {PATH}", "PATH", lastConfiguration);
Alexander Hansenad28e402025-06-25 12:38:20 +0200592 }
593 }
594 }
595 else
596 {
597 // not an error, just logging at this level to make it in the journal
Marc Olberdingb2f82822025-09-25 13:56:41 -0700598 std::error_code ec;
Alexander Hansen8feb0452025-09-15 14:29:20 +0200599 lg2::error("Clearing previous configuration");
Marc Olberdingb2f82822025-09-25 13:56:41 -0700600 std::filesystem::remove(currentConfiguration, ec);
Alexander Hansenad28e402025-06-25 12:38:20 +0200601 }
602}
603
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200604void EntityManager::registerCallback(const std::string& path)
James Feist75fdeeb2018-02-20 14:26:16 -0800605{
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200606 if (dbusMatches.contains(path))
607 {
608 return;
609 }
Nan Zhoua3315672022-09-20 19:48:14 +0000610
Alexander Hansen60803182025-06-27 14:41:28 +0200611 lg2::debug("creating PropertiesChanged match on {PATH}", "PATH", path);
612
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200613 std::function<void(sdbusplus::message_t & message)> eventHandler =
614 [&](sdbusplus::message_t&) { propertiesChangedCallback(); };
James Feistfd1264a2018-05-03 12:10:00 -0700615
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200616 sdbusplus::bus::match_t match(
617 static_cast<sdbusplus::bus_t&>(*systemBus),
618 "type='signal',member='PropertiesChanged',path='" + path + "'",
619 eventHandler);
620 dbusMatches.emplace(path, std::move(match));
621}
James Feistfd1264a2018-05-03 12:10:00 -0700622
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200623// We need a poke from DBus for static providers that create all their
624// objects prior to claiming a well-known name, and thus don't emit any
625// org.freedesktop.DBus.Properties signals. Similarly if a process exits
626// for any reason, expected or otherwise, we'll need a poke to remove
627// entities from DBus.
Christopher Meisf7252572025-06-11 13:22:05 +0200628void EntityManager::initFilters(
629 const std::unordered_set<std::string>& probeInterfaces)
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200630{
Alexander Hansen0f7bd782025-06-27 13:39:53 +0200631 nameOwnerChangedMatch = std::make_unique<sdbusplus::bus::match_t>(
Patrick Williams2af39222022-07-22 19:26:56 -0500632 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishopc76af0f2020-12-04 13:50:23 -0500633 sdbusplus::bus::match::rules::nameOwnerChanged(),
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200634 [this](sdbusplus::message_t& m) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400635 auto [name, oldOwner,
636 newOwner] = m.unpack<std::string, std::string, std::string>();
Patrick Williams7b8786f2022-10-10 10:23:37 -0500637
Patrick Williamsb7077432024-08-16 15:22:21 -0400638 if (name.starts_with(':'))
639 {
640 // We should do nothing with unique-name connections.
641 return;
642 }
Patrick Williams7b8786f2022-10-10 10:23:37 -0500643
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200644 propertiesChangedCallback();
Patrick Williamsb7077432024-08-16 15:22:21 -0400645 });
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200646
Brad Bishop10a8c5f2020-12-07 21:40:07 -0500647 // We also need a poke from DBus when new interfaces are created or
648 // destroyed.
Alexander Hansen0f7bd782025-06-27 13:39:53 +0200649 interfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
Patrick Williams2af39222022-07-22 19:26:56 -0500650 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -0500651 sdbusplus::bus::match::rules::interfacesAdded(),
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200652 [this, probeInterfaces](sdbusplus::message_t& msg) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400653 if (iaContainsProbeInterface(msg, probeInterfaces))
654 {
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200655 propertiesChangedCallback();
Patrick Williamsb7077432024-08-16 15:22:21 -0400656 }
657 });
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200658
Alexander Hansen0f7bd782025-06-27 13:39:53 +0200659 interfacesRemovedMatch = std::make_unique<sdbusplus::bus::match_t>(
Patrick Williams2af39222022-07-22 19:26:56 -0500660 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -0500661 sdbusplus::bus::match::rules::interfacesRemoved(),
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200662 [this, probeInterfaces](sdbusplus::message_t& msg) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400663 if (irContainsProbeInterface(msg, probeInterfaces))
664 {
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200665 propertiesChangedCallback();
Patrick Williamsb7077432024-08-16 15:22:21 -0400666 }
667 });
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200668}