xref: /openbmc/entity-manager/src/entity_manager/entity_manager.cpp (revision 7719269f0d269b1bf206545fcc645e86c9c42e90)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
3 
4 #include "entity_manager.hpp"
5 
6 #include "../utils.hpp"
7 #include "../variant_visitors.hpp"
8 #include "configuration.hpp"
9 #include "dbus_interface.hpp"
10 #include "log_device_inventory.hpp"
11 #include "overlay.hpp"
12 #include "perform_scan.hpp"
13 #include "topology.hpp"
14 #include "utils.hpp"
15 
16 #include <boost/asio/io_context.hpp>
17 #include <boost/asio/post.hpp>
18 #include <boost/asio/steady_timer.hpp>
19 #include <boost/container/flat_set.hpp>
20 #include <boost/range/iterator_range.hpp>
21 #include <nlohmann/json.hpp>
22 #include <phosphor-logging/lg2.hpp>
23 #include <sdbusplus/asio/connection.hpp>
24 #include <sdbusplus/asio/object_server.hpp>
25 #include <xyz/openbmc_project/Association/Definitions/common.hpp>
26 #include <xyz/openbmc_project/Inventory/Item/Bmc/common.hpp>
27 #include <xyz/openbmc_project/Inventory/Item/System/common.hpp>
28 #include <xyz/openbmc_project/Inventory/Item/common.hpp>
29 
30 #include <filesystem>
31 #include <flat_map>
32 #include <fstream>
33 #include <functional>
34 #include <map>
35 #include <regex>
36 constexpr const char* tempConfigDir = "/tmp/configuration/";
37 constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
38 
39 static constexpr std::array<const char*, 6> settableInterfaces = {
40     "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
41 
42 const std::regex illegalDbusPathRegex("[^A-Za-z0-9_.]");
43 const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
44 
getPermission(const std::string & interface)45 sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
46 {
47     return std::find(settableInterfaces.begin(), settableInterfaces.end(),
48                      interface) != settableInterfaces.end()
49                ? sdbusplus::asio::PropertyPermission::readWrite
50                : sdbusplus::asio::PropertyPermission::readOnly;
51 }
52 
EntityManager(std::shared_ptr<sdbusplus::asio::connection> & systemBus,boost::asio::io_context & io,const std::vector<std::filesystem::path> & configurationDirectories)53 EntityManager::EntityManager(
54     std::shared_ptr<sdbusplus::asio::connection>& systemBus,
55     boost::asio::io_context& io,
56     const std::vector<std::filesystem::path>& configurationDirectories) :
57     systemBus(systemBus),
58     objServer(sdbusplus::asio::object_server(systemBus, /*skipManager=*/true)),
59     configuration(configurationDirectories), lastJson(nlohmann::json::object()),
60     systemConfiguration(nlohmann::json::object()), io(io),
61     dbus_interface(io, objServer), powerStatus(*systemBus),
62     propertiesChangedTimer(io)
63 {
64     // All other objects that EntityManager currently support are under the
65     // inventory subtree.
66     // See the discussion at
67     // https://discord.com/channels/775381525260664832/1018929092009144380
68     objServer.add_manager("/xyz/openbmc_project/inventory");
69 
70     entityIface = objServer.add_interface("/xyz/openbmc_project/EntityManager",
71                                           "xyz.openbmc_project.EntityManager");
72     entityIface->register_method("ReScan", [this]() {
73         propertiesChangedCallback();
74     });
75     dbus_interface::tryIfaceInitialize(entityIface);
76 
77     initFilters(configuration.probeInterfaces);
78 }
79 
postToDbus(const nlohmann::json & newConfiguration)80 void EntityManager::postToDbus(const nlohmann::json& newConfiguration)
81 {
82     std::map<std::string, std::string> newBoards; // path -> name
83 
84     // iterate through boards
85     for (const auto& [boardId, boardConfig] : newConfiguration.items())
86     {
87         const nlohmann::json::object_t* boardConfigPtr =
88             boardConfig.get_ptr<const nlohmann::json::object_t*>();
89         if (boardConfigPtr == nullptr)
90         {
91             lg2::error("boardConfig for {BOARD} was not an object", "BOARD",
92                        boardId);
93             continue;
94         }
95         postBoardToDBus(boardId, *boardConfigPtr, newBoards);
96     }
97 
98     for (const auto& [assocPath, assocPropValue] :
99          topology.getAssocs(std::views::keys(newBoards)))
100     {
101         auto findBoard = newBoards.find(assocPath);
102         if (findBoard == newBoards.end())
103         {
104             continue;
105         }
106 
107         auto ifacePtr = dbus_interface.createInterface(
108             assocPath,
109             sdbusplus::common::xyz::openbmc_project::association::Definitions::
110                 interface,
111             findBoard->second);
112 
113         ifacePtr->register_property("Associations", assocPropValue);
114         dbus_interface::tryIfaceInitialize(ifacePtr);
115     }
116 }
117 
postBoardToDBus(const std::string & boardId,const nlohmann::json::object_t & boardConfig,std::map<std::string,std::string> & newBoards)118 void EntityManager::postBoardToDBus(
119     const std::string& boardId, const nlohmann::json::object_t& boardConfig,
120     std::map<std::string, std::string>& newBoards)
121 {
122     auto boardNameIt = boardConfig.find("Name");
123     if (boardNameIt == boardConfig.end())
124     {
125         lg2::error("Unable to find name for {BOARD}", "BOARD", boardId);
126         return;
127     }
128     const std::string* boardNamePtr =
129         boardNameIt->second.get_ptr<const std::string*>();
130     if (boardNamePtr == nullptr)
131     {
132         lg2::error("Name for {BOARD} was not a string", "BOARD", boardId);
133         return;
134     }
135     std::string boardName = *boardNamePtr;
136     std::string boardNameOrig = *boardNamePtr;
137     std::string jsonPointerPath = "/" + boardId;
138     // loop through newConfiguration, but use values from system
139     // configuration to be able to modify via dbus later
140     auto boardValues = systemConfiguration[boardId];
141     auto findBoardType = boardValues.find("Type");
142     std::string boardType;
143     if (findBoardType != boardValues.end() &&
144         findBoardType->type() == nlohmann::json::value_t::string)
145     {
146         boardType = findBoardType->get<std::string>();
147         std::regex_replace(boardType.begin(), boardType.begin(),
148                            boardType.end(), illegalDbusMemberRegex, "_");
149     }
150     else
151     {
152         lg2::error("Unable to find type for {BOARD} reverting to Chassis.",
153                    "BOARD", boardName);
154         boardType = "Chassis";
155     }
156 
157     const std::string boardPath =
158         em_utils::buildInventorySystemPath(boardName, boardType);
159 
160     std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
161         dbus_interface.createInterface(
162             boardPath,
163             sdbusplus::common::xyz::openbmc_project::inventory::Item::interface,
164             boardName);
165 
166     const std::string invItemIntf = std::format(
167         "{}.{}",
168         sdbusplus::common::xyz::openbmc_project::inventory::Item::interface,
169         boardType);
170 
171     std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
172         dbus_interface.createInterface(boardPath, invItemIntf, boardNameOrig);
173 
174     dbus_interface.createAddObjectMethod(jsonPointerPath, boardPath,
175                                          systemConfiguration, boardNameOrig);
176 
177     dbus_interface.populateInterfaceFromJson(
178         systemConfiguration, jsonPointerPath, boardIface, boardValues);
179     jsonPointerPath += "/";
180     // iterate through board properties
181     for (const auto& [propName, propValue] : boardValues.items())
182     {
183         if (propValue.type() == nlohmann::json::value_t::object)
184         {
185             std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
186                 dbus_interface.createInterface(boardPath, propName,
187                                                boardNameOrig);
188 
189             dbus_interface.populateInterfaceFromJson(
190                 systemConfiguration, jsonPointerPath + propName, iface,
191                 propValue);
192         }
193     }
194 
195     nlohmann::json::iterator exposes = boardValues.find("Exposes");
196     if (exposes == boardValues.end())
197     {
198         return;
199     }
200     // iterate through exposes
201     jsonPointerPath += "Exposes/";
202 
203     // store the board level pointer so we can modify it on the way down
204     std::string jsonPointerPathBoard = jsonPointerPath;
205     size_t exposesIndex = -1;
206     for (nlohmann::json& item : *exposes)
207     {
208         postExposesRecordsToDBus(item, exposesIndex, boardNameOrig,
209                                  jsonPointerPath, jsonPointerPathBoard,
210                                  boardPath, boardType);
211     }
212 
213     newBoards.emplace(boardPath, boardNameOrig);
214 }
215 
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)216 void EntityManager::postExposesRecordsToDBus(
217     nlohmann::json& item, size_t& exposesIndex,
218     const std::string& boardNameOrig, std::string jsonPointerPath,
219     const std::string& jsonPointerPathBoard, const std::string& boardPath,
220     const std::string& boardType)
221 {
222     exposesIndex++;
223     jsonPointerPath = jsonPointerPathBoard;
224     jsonPointerPath += std::to_string(exposesIndex);
225 
226     auto findName = item.find("Name");
227     if (findName == item.end())
228     {
229         lg2::error("cannot find name in field {ITEM}", "ITEM", item);
230         return;
231     }
232     auto findStatus = item.find("Status");
233     // if status is not found it is assumed to be status = 'okay'
234     if (findStatus != item.end())
235     {
236         if (*findStatus == "disabled")
237         {
238             return;
239         }
240     }
241     auto findType = item.find("Type");
242     std::string itemType;
243     if (findType != item.end())
244     {
245         itemType = findType->get<std::string>();
246         std::regex_replace(itemType.begin(), itemType.begin(), itemType.end(),
247                            illegalDbusPathRegex, "_");
248     }
249     else
250     {
251         itemType = "unknown";
252     }
253     std::string itemName = findName->get<std::string>();
254     std::regex_replace(itemName.begin(), itemName.begin(), itemName.end(),
255                        illegalDbusMemberRegex, "_");
256     std::string ifacePath = boardPath;
257     ifacePath += "/";
258     ifacePath += itemName;
259 
260     if (itemType == "BMC")
261     {
262         std::shared_ptr<sdbusplus::asio::dbus_interface> bmcIface =
263             dbus_interface.createInterface(
264                 ifacePath,
265                 sdbusplus::common::xyz::openbmc_project::inventory::item::Bmc::
266                     interface,
267                 boardNameOrig);
268         dbus_interface.populateInterfaceFromJson(
269             systemConfiguration, jsonPointerPath, bmcIface, item,
270             getPermission(itemType));
271     }
272     else if (itemType == "System")
273     {
274         std::shared_ptr<sdbusplus::asio::dbus_interface> systemIface =
275             dbus_interface.createInterface(
276                 ifacePath,
277                 sdbusplus::common::xyz::openbmc_project::inventory::item::
278                     System::interface,
279                 boardNameOrig);
280         dbus_interface.populateInterfaceFromJson(
281             systemConfiguration, jsonPointerPath, systemIface, item,
282             getPermission(itemType));
283     }
284 
285     for (const auto& [name, config] : item.items())
286     {
287         jsonPointerPath = jsonPointerPathBoard;
288         jsonPointerPath.append(std::to_string(exposesIndex))
289             .append("/")
290             .append(name);
291 
292         if (!postConfigurationRecord(name, config, boardNameOrig, itemType,
293                                      jsonPointerPath, ifacePath))
294         {
295             break;
296         }
297     }
298 
299     std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
300         dbus_interface.createInterface(
301             ifacePath, "xyz.openbmc_project.Configuration." + itemType,
302             boardNameOrig);
303 
304     dbus_interface.populateInterfaceFromJson(
305         systemConfiguration, jsonPointerPath, itemIface, item,
306         getPermission(itemType));
307 
308     topology.addBoard(boardPath, boardType, boardNameOrig, item);
309 }
310 
postConfigurationRecord(const std::string & name,nlohmann::json & config,const std::string & boardNameOrig,const std::string & itemType,const std::string & jsonPointerPath,const std::string & ifacePath)311 bool EntityManager::postConfigurationRecord(
312     const std::string& name, nlohmann::json& config,
313     const std::string& boardNameOrig, const std::string& itemType,
314     const std::string& jsonPointerPath, const std::string& ifacePath)
315 {
316     if (config.type() == nlohmann::json::value_t::object)
317     {
318         std::string ifaceName = "xyz.openbmc_project.Configuration.";
319         ifaceName.append(itemType).append(".").append(name);
320 
321         std::shared_ptr<sdbusplus::asio::dbus_interface> objectIface =
322             dbus_interface.createInterface(ifacePath, ifaceName, boardNameOrig);
323 
324         dbus_interface.populateInterfaceFromJson(
325             systemConfiguration, jsonPointerPath, objectIface, config,
326             getPermission(name));
327     }
328     else if (config.type() == nlohmann::json::value_t::array)
329     {
330         size_t index = 0;
331         if (config.empty())
332         {
333             return true;
334         }
335         bool isLegal = true;
336         auto type = config[0].type();
337         if (type != nlohmann::json::value_t::object)
338         {
339             return true;
340         }
341 
342         // verify legal json
343         for (const auto& arrayItem : config)
344         {
345             if (arrayItem.type() != type)
346             {
347                 isLegal = false;
348                 break;
349             }
350         }
351         if (!isLegal)
352         {
353             lg2::error("dbus format error {JSON}", "JSON", config);
354             return false;
355         }
356 
357         for (auto& arrayItem : config)
358         {
359             std::string ifaceName = "xyz.openbmc_project.Configuration.";
360             ifaceName.append(itemType).append(".").append(name);
361             ifaceName.append(std::to_string(index));
362 
363             std::shared_ptr<sdbusplus::asio::dbus_interface> objectIface =
364                 dbus_interface.createInterface(ifacePath, ifaceName,
365                                                boardNameOrig);
366 
367             dbus_interface.populateInterfaceFromJson(
368                 systemConfiguration,
369                 jsonPointerPath + "/" + std::to_string(index), objectIface,
370                 arrayItem, getPermission(name));
371             index++;
372         }
373     }
374 
375     return true;
376 }
377 
deviceRequiresPowerOn(const nlohmann::json & entity)378 static bool deviceRequiresPowerOn(const nlohmann::json& entity)
379 {
380     auto powerState = entity.find("PowerState");
381     if (powerState == entity.end())
382     {
383         return false;
384     }
385 
386     const auto* ptr = powerState->get_ptr<const std::string*>();
387     if (ptr == nullptr)
388     {
389         return false;
390     }
391 
392     return *ptr == "On" || *ptr == "BiosPost";
393 }
394 
pruneDevice(const nlohmann::json & systemConfiguration,const bool powerOff,const bool scannedPowerOff,const std::string & name,const nlohmann::json & device)395 static void pruneDevice(const nlohmann::json& systemConfiguration,
396                         const bool powerOff, const bool scannedPowerOff,
397                         const std::string& name, const nlohmann::json& device)
398 {
399     if (systemConfiguration.contains(name))
400     {
401         return;
402     }
403 
404     if (deviceRequiresPowerOn(device) && (powerOff || scannedPowerOff))
405     {
406         return;
407     }
408 
409     logDeviceRemoved(device);
410 }
411 
startRemovedTimer(boost::asio::steady_timer & timer,nlohmann::json & systemConfiguration)412 void EntityManager::startRemovedTimer(boost::asio::steady_timer& timer,
413                                       nlohmann::json& systemConfiguration)
414 {
415     if (systemConfiguration.empty() || lastJson.empty())
416     {
417         return; // not ready yet
418     }
419     if (scannedPowerOn)
420     {
421         return;
422     }
423 
424     if (!powerStatus.isPowerOn() && scannedPowerOff)
425     {
426         return;
427     }
428 
429     timer.expires_after(std::chrono::seconds(10));
430     timer.async_wait(
431         [&systemConfiguration, this](const boost::system::error_code& ec) {
432             if (ec == boost::asio::error::operation_aborted)
433             {
434                 return;
435             }
436 
437             bool powerOff = !powerStatus.isPowerOn();
438             for (const auto& [name, device] : lastJson.items())
439             {
440                 pruneDevice(systemConfiguration, powerOff, scannedPowerOff,
441                             name, device);
442             }
443 
444             scannedPowerOff = true;
445             if (!powerOff)
446             {
447                 scannedPowerOn = true;
448             }
449         });
450 }
451 
pruneConfiguration(bool powerOff,const std::string & name,const nlohmann::json & device)452 void EntityManager::pruneConfiguration(bool powerOff, const std::string& name,
453                                        const nlohmann::json& device)
454 {
455     if (powerOff && deviceRequiresPowerOn(device))
456     {
457         // power not on yet, don't know if it's there or not
458         return;
459     }
460 
461     auto& ifaces = dbus_interface.getDeviceInterfaces(device);
462     for (auto& iface : ifaces)
463     {
464         auto sharedPtr = iface.lock();
465         if (!!sharedPtr)
466         {
467             objServer.remove_interface(sharedPtr);
468         }
469     }
470 
471     ifaces.clear();
472     systemConfiguration.erase(name);
473     topology.remove(device["Name"].get<std::string>());
474     logDeviceRemoved(device);
475 }
476 
publishNewConfiguration(const size_t & instance,const size_t count,boost::asio::steady_timer & timer,const nlohmann::json newConfiguration)477 void EntityManager::publishNewConfiguration(
478     const size_t& instance, const size_t count,
479     boost::asio::steady_timer& timer, // Gerrit discussion:
480     // https://gerrit.openbmc-project.xyz/c/openbmc/entity-manager/+/52316/6
481     //
482     // Discord discussion:
483     // https://discord.com/channels/775381525260664832/867820390406422538/958048437729910854
484     //
485     // NOLINTNEXTLINE(performance-unnecessary-value-param)
486     const nlohmann::json newConfiguration)
487 {
488     loadOverlays(newConfiguration, io);
489 
490     boost::asio::post(io, [this]() {
491         if (!writeJsonFiles(systemConfiguration))
492         {
493             lg2::error("Error writing json files");
494         }
495     });
496 
497     boost::asio::post(io, [this, &instance, count, &timer, newConfiguration]() {
498         postToDbus(newConfiguration);
499         if (count == instance)
500         {
501             startRemovedTimer(timer, systemConfiguration);
502         }
503     });
504 }
505 
506 // main properties changed entry
propertiesChangedCallback()507 void EntityManager::propertiesChangedCallback()
508 {
509     propertiesChangedInstance++;
510     size_t count = propertiesChangedInstance;
511 
512     propertiesChangedTimer.expires_after(std::chrono::milliseconds(500));
513 
514     // setup an async wait as we normally get flooded with new requests
515     propertiesChangedTimer.async_wait(
516         [this, count](const boost::system::error_code& ec) {
517             if (ec == boost::asio::error::operation_aborted)
518             {
519                 // we were cancelled
520                 return;
521             }
522             if (ec)
523             {
524                 lg2::error("async wait error {ERR}", "ERR", ec.message());
525                 return;
526             }
527 
528             if (propertiesChangedInProgress)
529             {
530                 propertiesChangedCallback();
531                 return;
532             }
533             propertiesChangedInProgress = true;
534 
535             nlohmann::json oldConfiguration = systemConfiguration;
536             auto missingConfigurations = std::make_shared<nlohmann::json>();
537             *missingConfigurations = systemConfiguration;
538 
539             auto perfScan = std::make_shared<scan::PerformScan>(
540                 *this, *missingConfigurations, configuration.configurations, io,
541                 [this, count, oldConfiguration, missingConfigurations]() {
542                     // this is something that since ac has been applied to the
543                     // bmc we saw, and we no longer see it
544                     bool powerOff = !powerStatus.isPowerOn();
545                     for (const auto& [name, device] :
546                          missingConfigurations->items())
547                     {
548                         pruneConfiguration(powerOff, name, device);
549                     }
550                     nlohmann::json newConfiguration = systemConfiguration;
551 
552                     deriveNewConfiguration(oldConfiguration, newConfiguration);
553 
554                     for (const auto& [_, device] : newConfiguration.items())
555                     {
556                         logDeviceAdded(device);
557                     }
558 
559                     propertiesChangedInProgress = false;
560 
561                     boost::asio::post(io, [this, newConfiguration, count] {
562                         publishNewConfiguration(
563                             std::ref(propertiesChangedInstance), count,
564                             std::ref(propertiesChangedTimer), newConfiguration);
565                     });
566                 });
567             perfScan->run();
568         });
569 }
570 
571 // Check if InterfacesAdded payload contains an iface that needs probing.
iaContainsProbeInterface(sdbusplus::message_t & msg,const std::unordered_set<std::string> & probeInterfaces)572 static bool iaContainsProbeInterface(
573     sdbusplus::message_t& msg,
574     const std::unordered_set<std::string>& probeInterfaces)
575 {
576     sdbusplus::message::object_path path;
577     DBusObject interfaces;
578     msg.read(path, interfaces);
579     return std::ranges::any_of(interfaces | std::views::keys,
580                                [&probeInterfaces](const auto& ifaceName) {
581                                    return probeInterfaces.contains(ifaceName);
582                                });
583 }
584 
585 // Check if InterfacesRemoved payload contains an iface that needs probing.
irContainsProbeInterface(sdbusplus::message_t & msg,const std::unordered_set<std::string> & probeInterfaces)586 static bool irContainsProbeInterface(
587     sdbusplus::message_t& msg,
588     const std::unordered_set<std::string>& probeInterfaces)
589 {
590     sdbusplus::message::object_path path;
591     std::vector<std::string> interfaces;
592     msg.read(path, interfaces);
593     return std::ranges::any_of(interfaces,
594                                [&probeInterfaces](const auto& ifaceName) {
595                                    return probeInterfaces.contains(ifaceName);
596                                });
597 }
598 
handleCurrentConfigurationJson()599 void EntityManager::handleCurrentConfigurationJson()
600 {
601     if (EM_CACHE_CONFIGURATION && em_utils::fwVersionIsSame())
602     {
603         if (std::filesystem::is_regular_file(currentConfiguration))
604         {
605             // this file could just be deleted, but it's nice for debug
606             std::filesystem::create_directory(tempConfigDir);
607             std::filesystem::remove(lastConfiguration);
608             std::filesystem::copy(currentConfiguration, lastConfiguration);
609             std::filesystem::remove(currentConfiguration);
610 
611             std::ifstream jsonStream(lastConfiguration);
612             if (jsonStream.good())
613             {
614                 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
615                 if (data.is_discarded())
616                 {
617                     lg2::error("syntax error in {PATH}", "PATH",
618                                lastConfiguration);
619                 }
620                 else
621                 {
622                     lastJson = std::move(data);
623                 }
624             }
625             else
626             {
627                 lg2::error("unable to open {PATH}", "PATH", lastConfiguration);
628             }
629         }
630     }
631     else
632     {
633         // not an error, just logging at this level to make it in the journal
634         std::error_code ec;
635         lg2::error("Clearing previous configuration");
636         std::filesystem::remove(currentConfiguration, ec);
637     }
638 }
639 
registerCallback(const std::string & path)640 void EntityManager::registerCallback(const std::string& path)
641 {
642     if (dbusMatches.contains(path))
643     {
644         return;
645     }
646 
647     lg2::debug("creating PropertiesChanged match on {PATH}", "PATH", path);
648 
649     std::function<void(sdbusplus::message_t & message)> eventHandler =
650         [&](sdbusplus::message_t&) { propertiesChangedCallback(); };
651 
652     sdbusplus::bus::match_t match(
653         static_cast<sdbusplus::bus_t&>(*systemBus),
654         "type='signal',member='PropertiesChanged',path='" + path + "'",
655         eventHandler);
656     dbusMatches.emplace(path, std::move(match));
657 }
658 
659 // We need a poke from DBus for static providers that create all their
660 // objects prior to claiming a well-known name, and thus don't emit any
661 // org.freedesktop.DBus.Properties signals.  Similarly if a process exits
662 // for any reason, expected or otherwise, we'll need a poke to remove
663 // entities from DBus.
initFilters(const std::unordered_set<std::string> & probeInterfaces)664 void EntityManager::initFilters(
665     const std::unordered_set<std::string>& probeInterfaces)
666 {
667     nameOwnerChangedMatch = std::make_unique<sdbusplus::bus::match_t>(
668         static_cast<sdbusplus::bus_t&>(*systemBus),
669         sdbusplus::bus::match::rules::nameOwnerChanged(),
670         [this](sdbusplus::message_t& m) {
671             auto [name, oldOwner,
672                   newOwner] = m.unpack<std::string, std::string, std::string>();
673 
674             if (name.starts_with(':'))
675             {
676                 // We should do nothing with unique-name connections.
677                 return;
678             }
679 
680             propertiesChangedCallback();
681         });
682 
683     // We also need a poke from DBus when new interfaces are created or
684     // destroyed.
685     interfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
686         static_cast<sdbusplus::bus_t&>(*systemBus),
687         sdbusplus::bus::match::rules::interfacesAdded(),
688         [this, probeInterfaces](sdbusplus::message_t& msg) {
689             if (iaContainsProbeInterface(msg, probeInterfaces))
690             {
691                 propertiesChangedCallback();
692             }
693         });
694 
695     interfacesRemovedMatch = std::make_unique<sdbusplus::bus::match_t>(
696         static_cast<sdbusplus::bus_t&>(*systemBus),
697         sdbusplus::bus::match::rules::interfacesRemoved(),
698         [this, probeInterfaces](sdbusplus::message_t& msg) {
699             if (irContainsProbeInterface(msg, probeInterfaces))
700             {
701                 propertiesChangedCallback();
702             }
703         });
704 }
705