xref: /openbmc/entity-manager/src/entity_manager/entity_manager.cpp (revision e548bbe08b8205a905a1cde90c2a4c955149bfe4)
14e1142d6SAlexander Hansen // SPDX-License-Identifier: Apache-2.0
24e1142d6SAlexander Hansen // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
3fc9e7fdaSChristopher Meis 
4fc9e7fdaSChristopher Meis #include "entity_manager.hpp"
5fc9e7fdaSChristopher Meis 
6fc9e7fdaSChristopher Meis #include "../utils.hpp"
7fc9e7fdaSChristopher Meis #include "../variant_visitors.hpp"
8fc9e7fdaSChristopher Meis #include "configuration.hpp"
9fc9e7fdaSChristopher Meis #include "dbus_interface.hpp"
10f57a2596SAlexander Hansen #include "log_device_inventory.hpp"
11fc9e7fdaSChristopher Meis #include "overlay.hpp"
12fc9e7fdaSChristopher Meis #include "perform_scan.hpp"
13fc9e7fdaSChristopher Meis #include "topology.hpp"
1459ef1e72SChristopher Meis #include "utils.hpp"
15fc9e7fdaSChristopher Meis 
16fc9e7fdaSChristopher Meis #include <boost/asio/io_context.hpp>
17fc9e7fdaSChristopher Meis #include <boost/asio/post.hpp>
18fc9e7fdaSChristopher Meis #include <boost/asio/steady_timer.hpp>
19fc9e7fdaSChristopher Meis #include <boost/container/flat_set.hpp>
20fc9e7fdaSChristopher Meis #include <boost/range/iterator_range.hpp>
21fc9e7fdaSChristopher Meis #include <nlohmann/json.hpp>
228feb0454SAlexander Hansen #include <phosphor-logging/lg2.hpp>
23fc9e7fdaSChristopher Meis #include <sdbusplus/asio/connection.hpp>
24fc9e7fdaSChristopher Meis #include <sdbusplus/asio/object_server.hpp>
25c58af0b6SAlexander Hansen #include <xyz/openbmc_project/Association/Definitions/common.hpp>
26c58af0b6SAlexander Hansen #include <xyz/openbmc_project/Inventory/Item/Bmc/common.hpp>
27c58af0b6SAlexander Hansen #include <xyz/openbmc_project/Inventory/Item/System/common.hpp>
28c58af0b6SAlexander Hansen #include <xyz/openbmc_project/Inventory/Item/common.hpp>
29fc9e7fdaSChristopher Meis 
30fc9e7fdaSChristopher Meis #include <filesystem>
31dbf95b2cSEd Tanous #include <flat_map>
32fc9e7fdaSChristopher Meis #include <fstream>
33fc9e7fdaSChristopher Meis #include <functional>
34fc9e7fdaSChristopher Meis #include <map>
35fc9e7fdaSChristopher Meis #include <regex>
36fc9e7fdaSChristopher Meis constexpr const char* tempConfigDir = "/tmp/configuration/";
37fc9e7fdaSChristopher Meis constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
38fc9e7fdaSChristopher Meis 
39fc9e7fdaSChristopher Meis static constexpr std::array<const char*, 6> settableInterfaces = {
40fc9e7fdaSChristopher Meis     "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
41fc9e7fdaSChristopher Meis 
42fc9e7fdaSChristopher Meis const std::regex illegalDbusPathRegex("[^A-Za-z0-9_.]");
43fc9e7fdaSChristopher Meis const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
44fc9e7fdaSChristopher Meis 
getPermission(const std::string & interface)45fc9e7fdaSChristopher Meis sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
46fc9e7fdaSChristopher Meis {
47fc9e7fdaSChristopher Meis     return std::find(settableInterfaces.begin(), settableInterfaces.end(),
48fc9e7fdaSChristopher Meis                      interface) != settableInterfaces.end()
49fc9e7fdaSChristopher Meis                ? sdbusplus::asio::PropertyPermission::readWrite
50fc9e7fdaSChristopher Meis                : sdbusplus::asio::PropertyPermission::readOnly;
51fc9e7fdaSChristopher Meis }
52fc9e7fdaSChristopher Meis 
EntityManager(std::shared_ptr<sdbusplus::asio::connection> & systemBus,boost::asio::io_context & io,const std::vector<std::filesystem::path> & configurationDirectories,const std::filesystem::path & schemaDirectory)53cf6a75bdSChristopher Meis EntityManager::EntityManager(
54a555acf0SAlexander Hansen     std::shared_ptr<sdbusplus::asio::connection>& systemBus,
558290ca42SAlexander Hansen     boost::asio::io_context& io,
56bc0b05beSAlexander Hansen     const std::vector<std::filesystem::path>& configurationDirectories,
57bc0b05beSAlexander Hansen     const std::filesystem::path& schemaDirectory) :
58cf6a75bdSChristopher Meis     systemBus(systemBus),
59cf6a75bdSChristopher Meis     objServer(sdbusplus::asio::object_server(systemBus, /*skipManager=*/true)),
60bc0b05beSAlexander Hansen     configuration(configurationDirectories, schemaDirectory),
61bc0b05beSAlexander Hansen     lastJson(nlohmann::json::object()),
62b1340dacSAlexander Hansen     systemConfiguration(nlohmann::json::object()), io(io),
63bc0b05beSAlexander Hansen     dbus_interface(io, objServer, schemaDirectory), powerStatus(*systemBus),
6489737256SAlexander Hansen     propertiesChangedTimer(io)
65cf6a75bdSChristopher Meis {
66cf6a75bdSChristopher Meis     // All other objects that EntityManager currently support are under the
67cf6a75bdSChristopher Meis     // inventory subtree.
68cf6a75bdSChristopher Meis     // See the discussion at
69cf6a75bdSChristopher Meis     // https://discord.com/channels/775381525260664832/1018929092009144380
70cf6a75bdSChristopher Meis     objServer.add_manager("/xyz/openbmc_project/inventory");
71fc9e7fdaSChristopher Meis 
72cf6a75bdSChristopher Meis     entityIface = objServer.add_interface("/xyz/openbmc_project/EntityManager",
73cf6a75bdSChristopher Meis                                           "xyz.openbmc_project.EntityManager");
74cf6a75bdSChristopher Meis     entityIface->register_method("ReScan", [this]() {
75cf6a75bdSChristopher Meis         propertiesChangedCallback();
76cf6a75bdSChristopher Meis     });
77cf6a75bdSChristopher Meis     dbus_interface::tryIfaceInitialize(entityIface);
78f7252577SChristopher Meis 
79f7252577SChristopher Meis     initFilters(configuration.probeInterfaces);
80cf6a75bdSChristopher Meis }
81cf6a75bdSChristopher Meis 
postToDbus(const nlohmann::json & newConfiguration)82cf6a75bdSChristopher Meis void EntityManager::postToDbus(const nlohmann::json& newConfiguration)
83fc9e7fdaSChristopher Meis {
84fc9e7fdaSChristopher Meis     std::map<std::string, std::string> newBoards; // path -> name
85fc9e7fdaSChristopher Meis 
86fc9e7fdaSChristopher Meis     // iterate through boards
87fc9e7fdaSChristopher Meis     for (const auto& [boardId, boardConfig] : newConfiguration.items())
88fc9e7fdaSChristopher Meis     {
897719269fSEd Tanous         const nlohmann::json::object_t* boardConfigPtr =
907719269fSEd Tanous             boardConfig.get_ptr<const nlohmann::json::object_t*>();
917719269fSEd Tanous         if (boardConfigPtr == nullptr)
927719269fSEd Tanous         {
937719269fSEd Tanous             lg2::error("boardConfig for {BOARD} was not an object", "BOARD",
947719269fSEd Tanous                        boardId);
957719269fSEd Tanous             continue;
967719269fSEd Tanous         }
977719269fSEd Tanous         postBoardToDBus(boardId, *boardConfigPtr, newBoards);
984fcd946eSAlexander Hansen     }
994fcd946eSAlexander Hansen 
1004fcd946eSAlexander Hansen     for (const auto& [assocPath, assocPropValue] :
10132e7418cSAlexander Hansen          topology.getAssocs(std::views::keys(newBoards)))
1024fcd946eSAlexander Hansen     {
1034fcd946eSAlexander Hansen         auto findBoard = newBoards.find(assocPath);
1044fcd946eSAlexander Hansen         if (findBoard == newBoards.end())
1054fcd946eSAlexander Hansen         {
1064fcd946eSAlexander Hansen             continue;
1074fcd946eSAlexander Hansen         }
1084fcd946eSAlexander Hansen 
1094fcd946eSAlexander Hansen         auto ifacePtr = dbus_interface.createInterface(
110c58af0b6SAlexander Hansen             assocPath,
111c58af0b6SAlexander Hansen             sdbusplus::common::xyz::openbmc_project::association::Definitions::
112c58af0b6SAlexander Hansen                 interface,
1134fcd946eSAlexander Hansen             findBoard->second);
1144fcd946eSAlexander Hansen 
1154fcd946eSAlexander Hansen         ifacePtr->register_property("Associations", assocPropValue);
1164fcd946eSAlexander Hansen         dbus_interface::tryIfaceInitialize(ifacePtr);
1174fcd946eSAlexander Hansen     }
1184fcd946eSAlexander Hansen }
1194fcd946eSAlexander Hansen 
postBoardToDBus(const std::string & boardId,const nlohmann::json::object_t & boardConfig,std::map<std::string,std::string> & newBoards)1204fcd946eSAlexander Hansen void EntityManager::postBoardToDBus(
1217719269fSEd Tanous     const std::string& boardId, const nlohmann::json::object_t& boardConfig,
1224fcd946eSAlexander Hansen     std::map<std::string, std::string>& newBoards)
1234fcd946eSAlexander Hansen {
1247719269fSEd Tanous     auto boardNameIt = boardConfig.find("Name");
1257719269fSEd Tanous     if (boardNameIt == boardConfig.end())
1267719269fSEd Tanous     {
1277719269fSEd Tanous         lg2::error("Unable to find name for {BOARD}", "BOARD", boardId);
1287719269fSEd Tanous         return;
1297719269fSEd Tanous     }
1307719269fSEd Tanous     const std::string* boardNamePtr =
1317719269fSEd Tanous         boardNameIt->second.get_ptr<const std::string*>();
1327719269fSEd Tanous     if (boardNamePtr == nullptr)
1337719269fSEd Tanous     {
1347719269fSEd Tanous         lg2::error("Name for {BOARD} was not a string", "BOARD", boardId);
1357719269fSEd Tanous         return;
1367719269fSEd Tanous     }
1377719269fSEd Tanous     std::string boardName = *boardNamePtr;
1387719269fSEd Tanous     std::string boardNameOrig = *boardNamePtr;
139fc9e7fdaSChristopher Meis     std::string jsonPointerPath = "/" + boardId;
140fc9e7fdaSChristopher Meis     // loop through newConfiguration, but use values from system
141fc9e7fdaSChristopher Meis     // configuration to be able to modify via dbus later
142fc9e7fdaSChristopher Meis     auto boardValues = systemConfiguration[boardId];
143fc9e7fdaSChristopher Meis     auto findBoardType = boardValues.find("Type");
144fc9e7fdaSChristopher Meis     std::string boardType;
145fc9e7fdaSChristopher Meis     if (findBoardType != boardValues.end() &&
146fc9e7fdaSChristopher Meis         findBoardType->type() == nlohmann::json::value_t::string)
147fc9e7fdaSChristopher Meis     {
148fc9e7fdaSChristopher Meis         boardType = findBoardType->get<std::string>();
149fc9e7fdaSChristopher Meis         std::regex_replace(boardType.begin(), boardType.begin(),
150fc9e7fdaSChristopher Meis                            boardType.end(), illegalDbusMemberRegex, "_");
151fc9e7fdaSChristopher Meis     }
152fc9e7fdaSChristopher Meis     else
153fc9e7fdaSChristopher Meis     {
1548feb0454SAlexander Hansen         lg2::error("Unable to find type for {BOARD} reverting to Chassis.",
1558feb0454SAlexander Hansen                    "BOARD", boardName);
156fc9e7fdaSChristopher Meis         boardType = "Chassis";
157fc9e7fdaSChristopher Meis     }
158fc9e7fdaSChristopher Meis 
159064d8affSAlexander Hansen     lg2::debug("post {TYPE} '{NAME}' to DBus", "TYPE", boardType, "NAME",
160064d8affSAlexander Hansen                boardName);
161064d8affSAlexander Hansen 
162811160e1SChristopher Meis     const std::string boardPath =
163811160e1SChristopher Meis         em_utils::buildInventorySystemPath(boardName, boardType);
164fc9e7fdaSChristopher Meis 
165fc9e7fdaSChristopher Meis     std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
16689737256SAlexander Hansen         dbus_interface.createInterface(
167c58af0b6SAlexander Hansen             boardPath,
168c58af0b6SAlexander Hansen             sdbusplus::common::xyz::openbmc_project::inventory::Item::interface,
169*e548bbe0SEric Yang             boardNameOrig);
170c58af0b6SAlexander Hansen 
171c58af0b6SAlexander Hansen     const std::string invItemIntf = std::format(
172c58af0b6SAlexander Hansen         "{}.{}",
173c58af0b6SAlexander Hansen         sdbusplus::common::xyz::openbmc_project::inventory::Item::interface,
174c58af0b6SAlexander Hansen         boardType);
175fc9e7fdaSChristopher Meis 
176fc9e7fdaSChristopher Meis     std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
177c58af0b6SAlexander Hansen         dbus_interface.createInterface(boardPath, invItemIntf, boardNameOrig);
178fc9e7fdaSChristopher Meis 
17989737256SAlexander Hansen     dbus_interface.createAddObjectMethod(jsonPointerPath, boardPath,
18089737256SAlexander Hansen                                          systemConfiguration, boardNameOrig);
18189737256SAlexander Hansen 
18289737256SAlexander Hansen     dbus_interface.populateInterfaceFromJson(
18389737256SAlexander Hansen         systemConfiguration, jsonPointerPath, boardIface, boardValues);
184fc9e7fdaSChristopher Meis     jsonPointerPath += "/";
185fc9e7fdaSChristopher Meis     // iterate through board properties
186fc9e7fdaSChristopher Meis     for (const auto& [propName, propValue] : boardValues.items())
187fc9e7fdaSChristopher Meis     {
188fc9e7fdaSChristopher Meis         if (propValue.type() == nlohmann::json::value_t::object)
189fc9e7fdaSChristopher Meis         {
190fc9e7fdaSChristopher Meis             std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
19189737256SAlexander Hansen                 dbus_interface.createInterface(boardPath, propName,
1924fcd946eSAlexander Hansen                                                boardNameOrig);
193fc9e7fdaSChristopher Meis 
19489737256SAlexander Hansen             dbus_interface.populateInterfaceFromJson(
19589737256SAlexander Hansen                 systemConfiguration, jsonPointerPath + propName, iface,
19689737256SAlexander Hansen                 propValue);
197fc9e7fdaSChristopher Meis         }
198fc9e7fdaSChristopher Meis     }
199fc9e7fdaSChristopher Meis 
2004fa40126SAlexander Hansen     nlohmann::json::iterator exposes = boardValues.find("Exposes");
201fc9e7fdaSChristopher Meis     if (exposes == boardValues.end())
202fc9e7fdaSChristopher Meis     {
2034fcd946eSAlexander Hansen         return;
204fc9e7fdaSChristopher Meis     }
205fc9e7fdaSChristopher Meis     // iterate through exposes
206fc9e7fdaSChristopher Meis     jsonPointerPath += "Exposes/";
207fc9e7fdaSChristopher Meis 
208fc9e7fdaSChristopher Meis     // store the board level pointer so we can modify it on the way down
209fc9e7fdaSChristopher Meis     std::string jsonPointerPathBoard = jsonPointerPath;
210fc9e7fdaSChristopher Meis     size_t exposesIndex = -1;
2114fa40126SAlexander Hansen     for (nlohmann::json& item : *exposes)
2124fa40126SAlexander Hansen     {
2134fa40126SAlexander Hansen         postExposesRecordsToDBus(item, exposesIndex, boardNameOrig,
2144fa40126SAlexander Hansen                                  jsonPointerPath, jsonPointerPathBoard,
2154fa40126SAlexander Hansen                                  boardPath, boardType);
2164fa40126SAlexander Hansen     }
2174fa40126SAlexander Hansen 
2184fa40126SAlexander Hansen     newBoards.emplace(boardPath, boardNameOrig);
2194fa40126SAlexander Hansen }
2204fa40126SAlexander Hansen 
postExposesRecordsToDBus(nlohmann::json & item,size_t & exposesIndex,const std::string & boardNameOrig,std::string jsonPointerPath,const std::string & jsonPointerPathBoard,const std::string & boardPath,const std::string & boardType)2214fa40126SAlexander Hansen void EntityManager::postExposesRecordsToDBus(
2224fa40126SAlexander Hansen     nlohmann::json& item, size_t& exposesIndex,
2234fa40126SAlexander Hansen     const std::string& boardNameOrig, std::string jsonPointerPath,
2244fa40126SAlexander Hansen     const std::string& jsonPointerPathBoard, const std::string& boardPath,
2254fa40126SAlexander Hansen     const std::string& boardType)
226fc9e7fdaSChristopher Meis {
227fc9e7fdaSChristopher Meis     exposesIndex++;
228fc9e7fdaSChristopher Meis     jsonPointerPath = jsonPointerPathBoard;
229fc9e7fdaSChristopher Meis     jsonPointerPath += std::to_string(exposesIndex);
230fc9e7fdaSChristopher Meis 
231fc9e7fdaSChristopher Meis     auto findName = item.find("Name");
232fc9e7fdaSChristopher Meis     if (findName == item.end())
233fc9e7fdaSChristopher Meis     {
2348feb0454SAlexander Hansen         lg2::error("cannot find name in field {ITEM}", "ITEM", item);
2354fa40126SAlexander Hansen         return;
236fc9e7fdaSChristopher Meis     }
237fc9e7fdaSChristopher Meis     auto findStatus = item.find("Status");
238fc9e7fdaSChristopher Meis     // if status is not found it is assumed to be status = 'okay'
239fc9e7fdaSChristopher Meis     if (findStatus != item.end())
240fc9e7fdaSChristopher Meis     {
241fc9e7fdaSChristopher Meis         if (*findStatus == "disabled")
242fc9e7fdaSChristopher Meis         {
2434fa40126SAlexander Hansen             return;
244fc9e7fdaSChristopher Meis         }
245fc9e7fdaSChristopher Meis     }
246fc9e7fdaSChristopher Meis     auto findType = item.find("Type");
247fc9e7fdaSChristopher Meis     std::string itemType;
248fc9e7fdaSChristopher Meis     if (findType != item.end())
249fc9e7fdaSChristopher Meis     {
250fc9e7fdaSChristopher Meis         itemType = findType->get<std::string>();
2514fa40126SAlexander Hansen         std::regex_replace(itemType.begin(), itemType.begin(), itemType.end(),
2524fa40126SAlexander Hansen                            illegalDbusPathRegex, "_");
253fc9e7fdaSChristopher Meis     }
254fc9e7fdaSChristopher Meis     else
255fc9e7fdaSChristopher Meis     {
256fc9e7fdaSChristopher Meis         itemType = "unknown";
257fc9e7fdaSChristopher Meis     }
258fc9e7fdaSChristopher Meis     std::string itemName = findName->get<std::string>();
2594fcd946eSAlexander Hansen     std::regex_replace(itemName.begin(), itemName.begin(), itemName.end(),
2604fcd946eSAlexander Hansen                        illegalDbusMemberRegex, "_");
261fc9e7fdaSChristopher Meis     std::string ifacePath = boardPath;
262fc9e7fdaSChristopher Meis     ifacePath += "/";
263fc9e7fdaSChristopher Meis     ifacePath += itemName;
264fc9e7fdaSChristopher Meis 
265fc9e7fdaSChristopher Meis     if (itemType == "BMC")
266fc9e7fdaSChristopher Meis     {
267fc9e7fdaSChristopher Meis         std::shared_ptr<sdbusplus::asio::dbus_interface> bmcIface =
26857604edcSAlexander Hansen             dbus_interface.createInterface(
269c58af0b6SAlexander Hansen                 ifacePath,
270c58af0b6SAlexander Hansen                 sdbusplus::common::xyz::openbmc_project::inventory::item::Bmc::
271c58af0b6SAlexander Hansen                     interface,
2724fa40126SAlexander Hansen                 boardNameOrig);
27389737256SAlexander Hansen         dbus_interface.populateInterfaceFromJson(
27489737256SAlexander Hansen             systemConfiguration, jsonPointerPath, bmcIface, item,
2754fa40126SAlexander Hansen             getPermission(itemType));
276fc9e7fdaSChristopher Meis     }
277fc9e7fdaSChristopher Meis     else if (itemType == "System")
278fc9e7fdaSChristopher Meis     {
279fc9e7fdaSChristopher Meis         std::shared_ptr<sdbusplus::asio::dbus_interface> systemIface =
28057604edcSAlexander Hansen             dbus_interface.createInterface(
281c58af0b6SAlexander Hansen                 ifacePath,
282c58af0b6SAlexander Hansen                 sdbusplus::common::xyz::openbmc_project::inventory::item::
283c58af0b6SAlexander Hansen                     System::interface,
28489737256SAlexander Hansen                 boardNameOrig);
28589737256SAlexander Hansen         dbus_interface.populateInterfaceFromJson(
28689737256SAlexander Hansen             systemConfiguration, jsonPointerPath, systemIface, item,
28789737256SAlexander Hansen             getPermission(itemType));
288fc9e7fdaSChristopher Meis     }
289fc9e7fdaSChristopher Meis 
290fc9e7fdaSChristopher Meis     for (const auto& [name, config] : item.items())
291fc9e7fdaSChristopher Meis     {
292fc9e7fdaSChristopher Meis         jsonPointerPath = jsonPointerPathBoard;
293fc9e7fdaSChristopher Meis         jsonPointerPath.append(std::to_string(exposesIndex))
294fc9e7fdaSChristopher Meis             .append("/")
295fc9e7fdaSChristopher Meis             .append(name);
2965aa65d8fSAlexander Hansen 
2975aa65d8fSAlexander Hansen         if (!postConfigurationRecord(name, config, boardNameOrig, itemType,
2985aa65d8fSAlexander Hansen                                      jsonPointerPath, ifacePath))
2995aa65d8fSAlexander Hansen         {
3005aa65d8fSAlexander Hansen             break;
3015aa65d8fSAlexander Hansen         }
3025aa65d8fSAlexander Hansen     }
3035aa65d8fSAlexander Hansen 
3045aa65d8fSAlexander Hansen     std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
3055aa65d8fSAlexander Hansen         dbus_interface.createInterface(
30689737256SAlexander Hansen             ifacePath, "xyz.openbmc_project.Configuration." + itemType,
30789737256SAlexander Hansen             boardNameOrig);
3085aa65d8fSAlexander Hansen 
30989737256SAlexander Hansen     dbus_interface.populateInterfaceFromJson(
31089737256SAlexander Hansen         systemConfiguration, jsonPointerPath, itemIface, item,
3115aa65d8fSAlexander Hansen         getPermission(itemType));
3125aa65d8fSAlexander Hansen 
3135aa65d8fSAlexander Hansen     topology.addBoard(boardPath, boardType, boardNameOrig, item);
3145aa65d8fSAlexander Hansen }
3155aa65d8fSAlexander Hansen 
postConfigurationRecord(const std::string & name,nlohmann::json & config,const std::string & boardNameOrig,const std::string & itemType,const std::string & jsonPointerPath,const std::string & ifacePath)3165aa65d8fSAlexander Hansen bool EntityManager::postConfigurationRecord(
3175aa65d8fSAlexander Hansen     const std::string& name, nlohmann::json& config,
3185aa65d8fSAlexander Hansen     const std::string& boardNameOrig, const std::string& itemType,
3195aa65d8fSAlexander Hansen     const std::string& jsonPointerPath, const std::string& ifacePath)
3205aa65d8fSAlexander Hansen {
321fc9e7fdaSChristopher Meis     if (config.type() == nlohmann::json::value_t::object)
322fc9e7fdaSChristopher Meis     {
3234fcd946eSAlexander Hansen         std::string ifaceName = "xyz.openbmc_project.Configuration.";
324fc9e7fdaSChristopher Meis         ifaceName.append(itemType).append(".").append(name);
325fc9e7fdaSChristopher Meis 
3264fcd946eSAlexander Hansen         std::shared_ptr<sdbusplus::asio::dbus_interface> objectIface =
32789737256SAlexander Hansen             dbus_interface.createInterface(ifacePath, ifaceName, boardNameOrig);
328fc9e7fdaSChristopher Meis 
32989737256SAlexander Hansen         dbus_interface.populateInterfaceFromJson(
33089737256SAlexander Hansen             systemConfiguration, jsonPointerPath, objectIface, config,
33189737256SAlexander Hansen             getPermission(name));
332fc9e7fdaSChristopher Meis     }
333fc9e7fdaSChristopher Meis     else if (config.type() == nlohmann::json::value_t::array)
334fc9e7fdaSChristopher Meis     {
335fc9e7fdaSChristopher Meis         size_t index = 0;
336fc9e7fdaSChristopher Meis         if (config.empty())
337fc9e7fdaSChristopher Meis         {
3385aa65d8fSAlexander Hansen             return true;
339fc9e7fdaSChristopher Meis         }
340fc9e7fdaSChristopher Meis         bool isLegal = true;
341fc9e7fdaSChristopher Meis         auto type = config[0].type();
342fc9e7fdaSChristopher Meis         if (type != nlohmann::json::value_t::object)
343fc9e7fdaSChristopher Meis         {
3445aa65d8fSAlexander Hansen             return true;
345fc9e7fdaSChristopher Meis         }
346fc9e7fdaSChristopher Meis 
347fc9e7fdaSChristopher Meis         // verify legal json
348fc9e7fdaSChristopher Meis         for (const auto& arrayItem : config)
349fc9e7fdaSChristopher Meis         {
350fc9e7fdaSChristopher Meis             if (arrayItem.type() != type)
351fc9e7fdaSChristopher Meis             {
352fc9e7fdaSChristopher Meis                 isLegal = false;
353fc9e7fdaSChristopher Meis                 break;
354fc9e7fdaSChristopher Meis             }
355fc9e7fdaSChristopher Meis         }
356fc9e7fdaSChristopher Meis         if (!isLegal)
357fc9e7fdaSChristopher Meis         {
3588feb0454SAlexander Hansen             lg2::error("dbus format error {JSON}", "JSON", config);
3595aa65d8fSAlexander Hansen             return false;
360fc9e7fdaSChristopher Meis         }
361fc9e7fdaSChristopher Meis 
362fc9e7fdaSChristopher Meis         for (auto& arrayItem : config)
363fc9e7fdaSChristopher Meis         {
3644fa40126SAlexander Hansen             std::string ifaceName = "xyz.openbmc_project.Configuration.";
365fc9e7fdaSChristopher Meis             ifaceName.append(itemType).append(".").append(name);
366fc9e7fdaSChristopher Meis             ifaceName.append(std::to_string(index));
367fc9e7fdaSChristopher Meis 
3684fa40126SAlexander Hansen             std::shared_ptr<sdbusplus::asio::dbus_interface> objectIface =
36989737256SAlexander Hansen                 dbus_interface.createInterface(ifacePath, ifaceName,
3705aa65d8fSAlexander Hansen                                                boardNameOrig);
371fc9e7fdaSChristopher Meis 
37289737256SAlexander Hansen             dbus_interface.populateInterfaceFromJson(
37389737256SAlexander Hansen                 systemConfiguration,
3744fa40126SAlexander Hansen                 jsonPointerPath + "/" + std::to_string(index), objectIface,
37589737256SAlexander Hansen                 arrayItem, getPermission(name));
376fc9e7fdaSChristopher Meis             index++;
377fc9e7fdaSChristopher Meis         }
378fc9e7fdaSChristopher Meis     }
379fc9e7fdaSChristopher Meis 
3805aa65d8fSAlexander Hansen     return true;
381fc9e7fdaSChristopher Meis }
382fc9e7fdaSChristopher Meis 
deviceRequiresPowerOn(const nlohmann::json & entity)383fc9e7fdaSChristopher Meis static bool deviceRequiresPowerOn(const nlohmann::json& entity)
384fc9e7fdaSChristopher Meis {
385fc9e7fdaSChristopher Meis     auto powerState = entity.find("PowerState");
386fc9e7fdaSChristopher Meis     if (powerState == entity.end())
387fc9e7fdaSChristopher Meis     {
388fc9e7fdaSChristopher Meis         return false;
389fc9e7fdaSChristopher Meis     }
390fc9e7fdaSChristopher Meis 
391fc9e7fdaSChristopher Meis     const auto* ptr = powerState->get_ptr<const std::string*>();
392fc9e7fdaSChristopher Meis     if (ptr == nullptr)
393fc9e7fdaSChristopher Meis     {
394fc9e7fdaSChristopher Meis         return false;
395fc9e7fdaSChristopher Meis     }
396fc9e7fdaSChristopher Meis 
397fc9e7fdaSChristopher Meis     return *ptr == "On" || *ptr == "BiosPost";
398fc9e7fdaSChristopher Meis }
399fc9e7fdaSChristopher Meis 
pruneDevice(const nlohmann::json & systemConfiguration,const bool powerOff,const bool scannedPowerOff,const std::string & name,const nlohmann::json & device)400fc9e7fdaSChristopher Meis static void pruneDevice(const nlohmann::json& systemConfiguration,
401fc9e7fdaSChristopher Meis                         const bool powerOff, const bool scannedPowerOff,
402fc9e7fdaSChristopher Meis                         const std::string& name, const nlohmann::json& device)
403fc9e7fdaSChristopher Meis {
404fc9e7fdaSChristopher Meis     if (systemConfiguration.contains(name))
405fc9e7fdaSChristopher Meis     {
406fc9e7fdaSChristopher Meis         return;
407fc9e7fdaSChristopher Meis     }
408fc9e7fdaSChristopher Meis 
409fc9e7fdaSChristopher Meis     if (deviceRequiresPowerOn(device) && (powerOff || scannedPowerOff))
410fc9e7fdaSChristopher Meis     {
411fc9e7fdaSChristopher Meis         return;
412fc9e7fdaSChristopher Meis     }
413fc9e7fdaSChristopher Meis 
414fc9e7fdaSChristopher Meis     logDeviceRemoved(device);
415fc9e7fdaSChristopher Meis }
416fc9e7fdaSChristopher Meis 
startRemovedTimer(boost::asio::steady_timer & timer)4175febd663SAlexander Hansen void EntityManager::startRemovedTimer(boost::asio::steady_timer& timer)
418fc9e7fdaSChristopher Meis {
419fc9e7fdaSChristopher Meis     if (systemConfiguration.empty() || lastJson.empty())
420fc9e7fdaSChristopher Meis     {
421fc9e7fdaSChristopher Meis         return; // not ready yet
422fc9e7fdaSChristopher Meis     }
423fc9e7fdaSChristopher Meis     if (scannedPowerOn)
424fc9e7fdaSChristopher Meis     {
425fc9e7fdaSChristopher Meis         return;
426fc9e7fdaSChristopher Meis     }
427fc9e7fdaSChristopher Meis 
4280ab32b3fSAlexander Hansen     if (!powerStatus.isPowerOn() && scannedPowerOff)
429fc9e7fdaSChristopher Meis     {
430fc9e7fdaSChristopher Meis         return;
431fc9e7fdaSChristopher Meis     }
432fc9e7fdaSChristopher Meis 
433fc9e7fdaSChristopher Meis     timer.expires_after(std::chrono::seconds(10));
4345febd663SAlexander Hansen     timer.async_wait([this](const boost::system::error_code& ec) {
435fc9e7fdaSChristopher Meis         if (ec == boost::asio::error::operation_aborted)
436fc9e7fdaSChristopher Meis         {
437fc9e7fdaSChristopher Meis             return;
438fc9e7fdaSChristopher Meis         }
439fc9e7fdaSChristopher Meis 
4400ab32b3fSAlexander Hansen         bool powerOff = !powerStatus.isPowerOn();
441fc9e7fdaSChristopher Meis         for (const auto& [name, device] : lastJson.items())
442fc9e7fdaSChristopher Meis         {
4435febd663SAlexander Hansen             pruneDevice(systemConfiguration, powerOff, scannedPowerOff, name,
4445febd663SAlexander Hansen                         device);
445fc9e7fdaSChristopher Meis         }
446fc9e7fdaSChristopher Meis 
447fc9e7fdaSChristopher Meis         scannedPowerOff = true;
448fc9e7fdaSChristopher Meis         if (!powerOff)
449fc9e7fdaSChristopher Meis         {
450fc9e7fdaSChristopher Meis             scannedPowerOn = true;
451fc9e7fdaSChristopher Meis         }
452fc9e7fdaSChristopher Meis     });
453fc9e7fdaSChristopher Meis }
454fc9e7fdaSChristopher Meis 
pruneConfiguration(bool powerOff,const std::string & name,const nlohmann::json & device)455cf6a75bdSChristopher Meis void EntityManager::pruneConfiguration(bool powerOff, const std::string& name,
456fc9e7fdaSChristopher Meis                                        const nlohmann::json& device)
457fc9e7fdaSChristopher Meis {
458064d8affSAlexander Hansen     lg2::debug("pruning configuration");
459064d8affSAlexander Hansen 
460fc9e7fdaSChristopher Meis     if (powerOff && deviceRequiresPowerOn(device))
461fc9e7fdaSChristopher Meis     {
462fc9e7fdaSChristopher Meis         // power not on yet, don't know if it's there or not
463fc9e7fdaSChristopher Meis         return;
464fc9e7fdaSChristopher Meis     }
465fc9e7fdaSChristopher Meis 
46657604edcSAlexander Hansen     auto& ifaces = dbus_interface.getDeviceInterfaces(device);
467fc9e7fdaSChristopher Meis     for (auto& iface : ifaces)
468fc9e7fdaSChristopher Meis     {
469fc9e7fdaSChristopher Meis         auto sharedPtr = iface.lock();
470fc9e7fdaSChristopher Meis         if (!!sharedPtr)
471fc9e7fdaSChristopher Meis         {
472fc9e7fdaSChristopher Meis             objServer.remove_interface(sharedPtr);
473fc9e7fdaSChristopher Meis         }
474fc9e7fdaSChristopher Meis     }
475fc9e7fdaSChristopher Meis 
476fc9e7fdaSChristopher Meis     ifaces.clear();
477fc9e7fdaSChristopher Meis     systemConfiguration.erase(name);
478fc9e7fdaSChristopher Meis     topology.remove(device["Name"].get<std::string>());
479fc9e7fdaSChristopher Meis     logDeviceRemoved(device);
480fc9e7fdaSChristopher Meis }
481fc9e7fdaSChristopher Meis 
publishNewConfiguration(const size_t & instance,const size_t count,boost::asio::steady_timer & timer,const nlohmann::json newConfiguration)482cf6a75bdSChristopher Meis void EntityManager::publishNewConfiguration(
483fc9e7fdaSChristopher Meis     const size_t& instance, const size_t count,
484cf6a75bdSChristopher Meis     boost::asio::steady_timer& timer, // Gerrit discussion:
485fc9e7fdaSChristopher Meis     // https://gerrit.openbmc-project.xyz/c/openbmc/entity-manager/+/52316/6
486fc9e7fdaSChristopher Meis     //
487fc9e7fdaSChristopher Meis     // Discord discussion:
488fc9e7fdaSChristopher Meis     // https://discord.com/channels/775381525260664832/867820390406422538/958048437729910854
489fc9e7fdaSChristopher Meis     //
490fc9e7fdaSChristopher Meis     // NOLINTNEXTLINE(performance-unnecessary-value-param)
491cf6a75bdSChristopher Meis     const nlohmann::json newConfiguration)
492fc9e7fdaSChristopher Meis {
493a555acf0SAlexander Hansen     loadOverlays(newConfiguration, io);
494fc9e7fdaSChristopher Meis 
495cf6a75bdSChristopher Meis     boost::asio::post(io, [this]() {
496f7252577SChristopher Meis         if (!writeJsonFiles(systemConfiguration))
497fc9e7fdaSChristopher Meis         {
4988feb0454SAlexander Hansen             lg2::error("Error writing json files");
499fc9e7fdaSChristopher Meis         }
500fc9e7fdaSChristopher Meis     });
501fc9e7fdaSChristopher Meis 
502cf6a75bdSChristopher Meis     boost::asio::post(io, [this, &instance, count, &timer, newConfiguration]() {
503cf6a75bdSChristopher Meis         postToDbus(newConfiguration);
504fc9e7fdaSChristopher Meis         if (count == instance)
505fc9e7fdaSChristopher Meis         {
5065febd663SAlexander Hansen             startRemovedTimer(timer);
507fc9e7fdaSChristopher Meis         }
508fc9e7fdaSChristopher Meis     });
509fc9e7fdaSChristopher Meis }
510fc9e7fdaSChristopher Meis 
511fc9e7fdaSChristopher Meis // main properties changed entry
propertiesChangedCallback()512cf6a75bdSChristopher Meis void EntityManager::propertiesChangedCallback()
513fc9e7fdaSChristopher Meis {
5148c7951b6SAlexander Hansen     lg2::debug("properties changed callback");
515fc1b1e29SAlexander Hansen     propertiesChangedInstance++;
516fc1b1e29SAlexander Hansen     size_t count = propertiesChangedInstance;
517fc9e7fdaSChristopher Meis 
518b1340dacSAlexander Hansen     propertiesChangedTimer.expires_after(std::chrono::milliseconds(500));
519fc9e7fdaSChristopher Meis 
520fc9e7fdaSChristopher Meis     // setup an async wait as we normally get flooded with new requests
521b1340dacSAlexander Hansen     propertiesChangedTimer.async_wait(
522b1340dacSAlexander Hansen         [this, count](const boost::system::error_code& ec) {
5238c7951b6SAlexander Hansen             lg2::debug("properties changed callback timer expired");
524fc9e7fdaSChristopher Meis             if (ec == boost::asio::error::operation_aborted)
525fc9e7fdaSChristopher Meis             {
526fc9e7fdaSChristopher Meis                 // we were cancelled
527fc9e7fdaSChristopher Meis                 return;
528fc9e7fdaSChristopher Meis             }
529fc9e7fdaSChristopher Meis             if (ec)
530fc9e7fdaSChristopher Meis             {
5318feb0454SAlexander Hansen                 lg2::error("async wait error {ERR}", "ERR", ec.message());
532fc9e7fdaSChristopher Meis                 return;
533fc9e7fdaSChristopher Meis             }
534fc9e7fdaSChristopher Meis 
53516d4002dSAlexander Hansen             if (propertiesChangedInProgress)
536fc9e7fdaSChristopher Meis             {
537cf6a75bdSChristopher Meis                 propertiesChangedCallback();
538fc9e7fdaSChristopher Meis                 return;
539fc9e7fdaSChristopher Meis             }
54016d4002dSAlexander Hansen             propertiesChangedInProgress = true;
541fc9e7fdaSChristopher Meis 
5428c7951b6SAlexander Hansen             lg2::debug("properties changed callback in progress");
5438c7951b6SAlexander Hansen 
544fc9e7fdaSChristopher Meis             nlohmann::json oldConfiguration = systemConfiguration;
545fc9e7fdaSChristopher Meis             auto missingConfigurations = std::make_shared<nlohmann::json>();
546fc9e7fdaSChristopher Meis             *missingConfigurations = systemConfiguration;
547fc9e7fdaSChristopher Meis 
548fc9e7fdaSChristopher Meis             auto perfScan = std::make_shared<scan::PerformScan>(
549f7252577SChristopher Meis                 *this, *missingConfigurations, configuration.configurations, io,
550cf6a75bdSChristopher Meis                 [this, count, oldConfiguration, missingConfigurations]() {
551b1340dacSAlexander Hansen                     // this is something that since ac has been applied to the
552b1340dacSAlexander Hansen                     // bmc we saw, and we no longer see it
5530ab32b3fSAlexander Hansen                     bool powerOff = !powerStatus.isPowerOn();
554fc9e7fdaSChristopher Meis                     for (const auto& [name, device] :
555fc9e7fdaSChristopher Meis                          missingConfigurations->items())
556fc9e7fdaSChristopher Meis                     {
557cf6a75bdSChristopher Meis                         pruneConfiguration(powerOff, name, device);
558fc9e7fdaSChristopher Meis                     }
559fc9e7fdaSChristopher Meis                     nlohmann::json newConfiguration = systemConfiguration;
560fc9e7fdaSChristopher Meis 
561f7252577SChristopher Meis                     deriveNewConfiguration(oldConfiguration, newConfiguration);
562fc9e7fdaSChristopher Meis 
563fc9e7fdaSChristopher Meis                     for (const auto& [_, device] : newConfiguration.items())
564fc9e7fdaSChristopher Meis                     {
565fc9e7fdaSChristopher Meis                         logDeviceAdded(device);
566fc9e7fdaSChristopher Meis                     }
567fc9e7fdaSChristopher Meis 
56816d4002dSAlexander Hansen                     propertiesChangedInProgress = false;
569fc9e7fdaSChristopher Meis 
570cf6a75bdSChristopher Meis                     boost::asio::post(io, [this, newConfiguration, count] {
571b1340dacSAlexander Hansen                         publishNewConfiguration(
572fc1b1e29SAlexander Hansen                             std::ref(propertiesChangedInstance), count,
573b1340dacSAlexander Hansen                             std::ref(propertiesChangedTimer), newConfiguration);
574cf6a75bdSChristopher Meis                     });
575fc9e7fdaSChristopher Meis                 });
576fc9e7fdaSChristopher Meis             perfScan->run();
577fc9e7fdaSChristopher Meis         });
578fc9e7fdaSChristopher Meis }
579fc9e7fdaSChristopher Meis 
580fc9e7fdaSChristopher Meis // Check if InterfacesAdded payload contains an iface that needs probing.
iaContainsProbeInterface(sdbusplus::message_t & msg,const std::unordered_set<std::string> & probeInterfaces)581fc9e7fdaSChristopher Meis static bool iaContainsProbeInterface(
582f7252577SChristopher Meis     sdbusplus::message_t& msg,
583f7252577SChristopher Meis     const std::unordered_set<std::string>& probeInterfaces)
584fc9e7fdaSChristopher Meis {
585fc9e7fdaSChristopher Meis     sdbusplus::message::object_path path;
586fc9e7fdaSChristopher Meis     DBusObject interfaces;
587fc9e7fdaSChristopher Meis     msg.read(path, interfaces);
588802cbf21SEldin Lee     return std::ranges::any_of(interfaces | std::views::keys,
589802cbf21SEldin Lee                                [&probeInterfaces](const auto& ifaceName) {
590802cbf21SEldin Lee                                    return probeInterfaces.contains(ifaceName);
591fc9e7fdaSChristopher Meis                                });
592fc9e7fdaSChristopher Meis }
593fc9e7fdaSChristopher Meis 
594fc9e7fdaSChristopher Meis // Check if InterfacesRemoved payload contains an iface that needs probing.
irContainsProbeInterface(const std::vector<std::string> & interfaces,const std::unordered_set<std::string> & probeInterfaces)595fc9e7fdaSChristopher Meis static bool irContainsProbeInterface(
596b7a74178SEric Yang     const std::vector<std::string>& interfaces,
597f7252577SChristopher Meis     const std::unordered_set<std::string>& probeInterfaces)
598fc9e7fdaSChristopher Meis {
599802cbf21SEldin Lee     return std::ranges::any_of(interfaces,
600802cbf21SEldin Lee                                [&probeInterfaces](const auto& ifaceName) {
601802cbf21SEldin Lee                                    return probeInterfaces.contains(ifaceName);
602802cbf21SEldin Lee                                });
603fc9e7fdaSChristopher Meis }
604fc9e7fdaSChristopher Meis 
handleCurrentConfigurationJson()605ad28e406SAlexander Hansen void EntityManager::handleCurrentConfigurationJson()
606ad28e406SAlexander Hansen {
607e665185bSAlexander Hansen     if (EM_CACHE_CONFIGURATION && em_utils::fwVersionIsSame())
608ad28e406SAlexander Hansen     {
609f7252577SChristopher Meis         if (std::filesystem::is_regular_file(currentConfiguration))
610ad28e406SAlexander Hansen         {
611ad28e406SAlexander Hansen             // this file could just be deleted, but it's nice for debug
612ad28e406SAlexander Hansen             std::filesystem::create_directory(tempConfigDir);
613ad28e406SAlexander Hansen             std::filesystem::remove(lastConfiguration);
614f7252577SChristopher Meis             std::filesystem::copy(currentConfiguration, lastConfiguration);
615f7252577SChristopher Meis             std::filesystem::remove(currentConfiguration);
616ad28e406SAlexander Hansen 
617ad28e406SAlexander Hansen             std::ifstream jsonStream(lastConfiguration);
618ad28e406SAlexander Hansen             if (jsonStream.good())
619ad28e406SAlexander Hansen             {
620ad28e406SAlexander Hansen                 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
621ad28e406SAlexander Hansen                 if (data.is_discarded())
622ad28e406SAlexander Hansen                 {
6238feb0454SAlexander Hansen                     lg2::error("syntax error in {PATH}", "PATH",
6248feb0454SAlexander Hansen                                lastConfiguration);
625ad28e406SAlexander Hansen                 }
626ad28e406SAlexander Hansen                 else
627ad28e406SAlexander Hansen                 {
628ad28e406SAlexander Hansen                     lastJson = std::move(data);
629ad28e406SAlexander Hansen                 }
630ad28e406SAlexander Hansen             }
631ad28e406SAlexander Hansen             else
632ad28e406SAlexander Hansen             {
6338feb0454SAlexander Hansen                 lg2::error("unable to open {PATH}", "PATH", lastConfiguration);
634ad28e406SAlexander Hansen             }
635ad28e406SAlexander Hansen         }
636ad28e406SAlexander Hansen     }
637ad28e406SAlexander Hansen     else
638ad28e406SAlexander Hansen     {
639ad28e406SAlexander Hansen         // not an error, just logging at this level to make it in the journal
640b2f82829SMarc Olberding         std::error_code ec;
6418feb0454SAlexander Hansen         lg2::error("Clearing previous configuration");
642b2f82829SMarc Olberding         std::filesystem::remove(currentConfiguration, ec);
643ad28e406SAlexander Hansen     }
644ad28e406SAlexander Hansen }
645ad28e406SAlexander Hansen 
registerCallback(const std::string & path)646cf6a75bdSChristopher Meis void EntityManager::registerCallback(const std::string& path)
647fc9e7fdaSChristopher Meis {
648cf6a75bdSChristopher Meis     if (dbusMatches.contains(path))
649cf6a75bdSChristopher Meis     {
650cf6a75bdSChristopher Meis         return;
651cf6a75bdSChristopher Meis     }
652fc9e7fdaSChristopher Meis 
65360803182SAlexander Hansen     lg2::debug("creating PropertiesChanged match on {PATH}", "PATH", path);
65460803182SAlexander Hansen 
655cf6a75bdSChristopher Meis     std::function<void(sdbusplus::message_t & message)> eventHandler =
656cf6a75bdSChristopher Meis         [&](sdbusplus::message_t&) { propertiesChangedCallback(); };
657fc9e7fdaSChristopher Meis 
658cf6a75bdSChristopher Meis     sdbusplus::bus::match_t match(
659cf6a75bdSChristopher Meis         static_cast<sdbusplus::bus_t&>(*systemBus),
660cf6a75bdSChristopher Meis         "type='signal',member='PropertiesChanged',path='" + path + "'",
661cf6a75bdSChristopher Meis         eventHandler);
662cf6a75bdSChristopher Meis     dbusMatches.emplace(path, std::move(match));
663cf6a75bdSChristopher Meis }
664fc9e7fdaSChristopher Meis 
665fc9e7fdaSChristopher Meis // We need a poke from DBus for static providers that create all their
666fc9e7fdaSChristopher Meis // objects prior to claiming a well-known name, and thus don't emit any
667fc9e7fdaSChristopher Meis // org.freedesktop.DBus.Properties signals.  Similarly if a process exits
668fc9e7fdaSChristopher Meis // for any reason, expected or otherwise, we'll need a poke to remove
669fc9e7fdaSChristopher Meis // entities from DBus.
initFilters(const std::unordered_set<std::string> & probeInterfaces)670f7252577SChristopher Meis void EntityManager::initFilters(
671f7252577SChristopher Meis     const std::unordered_set<std::string>& probeInterfaces)
672cf6a75bdSChristopher Meis {
6730f7bd780SAlexander Hansen     nameOwnerChangedMatch = std::make_unique<sdbusplus::bus::match_t>(
674fc9e7fdaSChristopher Meis         static_cast<sdbusplus::bus_t&>(*systemBus),
675fc9e7fdaSChristopher Meis         sdbusplus::bus::match::rules::nameOwnerChanged(),
676cf6a75bdSChristopher Meis         [this](sdbusplus::message_t& m) {
677fc9e7fdaSChristopher Meis             auto [name, oldOwner,
678fc9e7fdaSChristopher Meis                   newOwner] = m.unpack<std::string, std::string, std::string>();
679fc9e7fdaSChristopher Meis 
680fc9e7fdaSChristopher Meis             if (name.starts_with(':'))
681fc9e7fdaSChristopher Meis             {
682fc9e7fdaSChristopher Meis                 // We should do nothing with unique-name connections.
683fc9e7fdaSChristopher Meis                 return;
684fc9e7fdaSChristopher Meis             }
685fc9e7fdaSChristopher Meis 
686cf6a75bdSChristopher Meis             propertiesChangedCallback();
687fc9e7fdaSChristopher Meis         });
688cf6a75bdSChristopher Meis 
689fc9e7fdaSChristopher Meis     // We also need a poke from DBus when new interfaces are created or
690fc9e7fdaSChristopher Meis     // destroyed.
6910f7bd780SAlexander Hansen     interfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
692fc9e7fdaSChristopher Meis         static_cast<sdbusplus::bus_t&>(*systemBus),
693fc9e7fdaSChristopher Meis         sdbusplus::bus::match::rules::interfacesAdded(),
694cf6a75bdSChristopher Meis         [this, probeInterfaces](sdbusplus::message_t& msg) {
695fc9e7fdaSChristopher Meis             if (iaContainsProbeInterface(msg, probeInterfaces))
696fc9e7fdaSChristopher Meis             {
697cf6a75bdSChristopher Meis                 propertiesChangedCallback();
698fc9e7fdaSChristopher Meis             }
699fc9e7fdaSChristopher Meis         });
700cf6a75bdSChristopher Meis 
7010f7bd780SAlexander Hansen     interfacesRemovedMatch = std::make_unique<sdbusplus::bus::match_t>(
702fc9e7fdaSChristopher Meis         static_cast<sdbusplus::bus_t&>(*systemBus),
703fc9e7fdaSChristopher Meis         sdbusplus::bus::match::rules::interfacesRemoved(),
704cf6a75bdSChristopher Meis         [this, probeInterfaces](sdbusplus::message_t& msg) {
705b7a74178SEric Yang             auto [path, interfaces] =
706b7a74178SEric Yang                 msg.unpack<sdbusplus::message::object_path,
707b7a74178SEric Yang                            std::vector<std::string>>();
708b7a74178SEric Yang 
709b7a74178SEric Yang             if (irContainsProbeInterface(interfaces, probeInterfaces))
710fc9e7fdaSChristopher Meis             {
711b7a74178SEric Yang                 // Clean up match on probe interface removal to avoid leaks
712b7a74178SEric Yang                 dbusMatches.erase(path.str);
713cf6a75bdSChristopher Meis                 propertiesChangedCallback();
714fc9e7fdaSChristopher Meis             }
715fc9e7fdaSChristopher Meis         });
716cf6a75bdSChristopher Meis }
717