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