xref: /openbmc/entity-manager/src/gpio-presence/config_provider.cpp (revision 8c4b1d999a13b27d6edc84cfaff1112a0cc07091)
1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2022-2024. All rights
3  * reserved. SPDX-License-Identifier: Apache-2.0
4  */
5 #include "config_provider.hpp"
6 
7 #include <boost/container/flat_map.hpp>
8 #include <phosphor-logging/lg2.hpp>
9 #include <sdbusplus/async/match.hpp>
10 #include <sdbusplus/bus/match.hpp>
11 #include <xyz/openbmc_project/ObjectMapper/client.hpp>
12 
13 #include <ranges>
14 #include <string>
15 
16 PHOSPHOR_LOG2_USING;
17 
18 using VariantType =
19     std::variant<std::vector<std::string>, std::string, int64_t, uint64_t,
20                  double, int32_t, uint32_t, int16_t, uint16_t, uint8_t, bool>;
21 using ConfigMap = boost::container::flat_map<std::string, VariantType>;
22 using ConfigData = boost::container::flat_map<std::string, ConfigMap>;
23 
24 namespace gpio_presence
25 {
26 
ConfigProvider(sdbusplus::async::context & ctx,const std::string & interface)27 ConfigProvider::ConfigProvider(sdbusplus::async::context& ctx,
28                                const std::string& interface) :
29     interface(interface), ctx(ctx)
30 {}
31 
initialize(AddedCallback addConfig,RemovedCallback removeConfig)32 auto ConfigProvider::initialize(AddedCallback addConfig,
33                                 RemovedCallback removeConfig)
34     -> sdbusplus::async::task<void>
35 {
36     ctx.spawn(handleInterfacesAdded(addConfig));
37     ctx.spawn(handleInterfacesRemoved(removeConfig));
38 
39     co_await getConfig(addConfig);
40 }
41 
getConfig(AddedCallback addConfig)42 auto ConfigProvider::getConfig(AddedCallback addConfig)
43     -> sdbusplus::async::task<void>
44 {
45     auto client = sdbusplus::client::xyz::openbmc_project::ObjectMapper<>(ctx)
46                       .service("xyz.openbmc_project.ObjectMapper")
47                       .path("/xyz/openbmc_project/object_mapper");
48 
49     debug("calling 'GetSubTree' to find instances of {INTF}", "INTF",
50           interface);
51 
52     using SubTreeType =
53         std::map<std::string, std::map<std::string, std::vector<std::string>>>;
54     SubTreeType res = {};
55 
56     try
57     {
58         std::vector<std::string> interfaces = {interface};
59         res = co_await client.get_sub_tree("/xyz/openbmc_project/inventory", 0,
60                                            interfaces);
61     }
62     catch (std::exception& e)
63     {
64         error("Failed GetSubTree call for configuration interface: {ERR}",
65               "ERR", e);
66     }
67 
68     if (res.empty())
69     {
70         co_return;
71     }
72 
73     // call the user callback for all the device that is already available
74     for (auto& [path, serviceInterfaceMap] : res)
75     {
76         for (const auto& service :
77              std::ranges::views::keys(serviceInterfaceMap))
78         {
79             debug("found configuration interface at {SERVICE} {PATH} {INTF}",
80                   "SERVICE", service, "PATH", path, "INTF", interface);
81 
82             addConfig(path);
83         }
84     }
85 }
86 
87 namespace rules_intf = sdbusplus::bus::match::rules;
88 
89 const auto senderRule = rules_intf::sender("xyz.openbmc_project.EntityManager");
90 
handleInterfacesAdded(AddedCallback addConfig)91 auto ConfigProvider::handleInterfacesAdded(AddedCallback addConfig)
92     -> sdbusplus::async::task<void>
93 {
94     debug("setting up dbus match for interfaces added");
95 
96     sdbusplus::async::match addedMatch(
97         ctx, rules_intf::interfacesAdded() + senderRule);
98 
99     while (!ctx.stop_requested())
100     {
101         auto [objPath, intfMap] =
102             co_await addedMatch
103                 .next<sdbusplus::message::object_path, ConfigData>();
104 
105         debug("Detected interface added on {OBJPATH}", "OBJPATH", objPath);
106 
107         if (!std::ranges::contains(std::views::keys(intfMap), interface))
108         {
109             continue;
110         }
111 
112         try
113         {
114             addConfig(objPath);
115         }
116         catch (std::exception& e)
117         {
118             error("Incomplete or invalid config found: {ERR}", "ERR", e);
119         }
120     }
121 };
122 
handleInterfacesRemoved(RemovedCallback removeConfig)123 auto ConfigProvider::handleInterfacesRemoved(RemovedCallback removeConfig)
124     -> sdbusplus::async::task<void>
125 {
126     debug("setting up dbus match for interfaces removed");
127 
128     sdbusplus::async::match removedMatch(
129         ctx, rules_intf::interfacesRemoved() + senderRule);
130 
131     while (!ctx.stop_requested())
132     {
133         auto [objectPath, interfaces] =
134             co_await removedMatch.next<sdbusplus::message::object_path,
135                                        std::vector<std::string>>();
136 
137         if (!std::ranges::contains(interfaces, interface))
138         {
139             continue;
140         }
141 
142         debug("Detected interface {INTF} removed on {OBJPATH}", "INTF",
143               interface, "OBJPATH", objectPath);
144 
145         removeConfig(objectPath);
146     }
147 };
148 
149 } // namespace gpio_presence
150