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