xref: /openbmc/entity-manager/src/gpio-presence/config_provider.cpp (revision 648320051f7e2e38766829c308384d503691d284)
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 tmp = co_await addedMatch
102                        .next<sdbusplus::message::object_path, ConfigData>();
103 
104         auto [objPath, intfMap] = std::move(tmp);
105 
106         debug("Detected interface added on {OBJPATH}", "OBJPATH", objPath);
107 
108         if (!std::ranges::contains(std::views::keys(intfMap), interface))
109         {
110             continue;
111         }
112 
113         try
114         {
115             addConfig(objPath);
116         }
117         catch (std::exception& e)
118         {
119             error("Incomplete or invalid config found: {ERR}", "ERR", e);
120         }
121     }
122 };
123 
handleInterfacesRemoved(RemovedCallback removeConfig)124 auto ConfigProvider::handleInterfacesRemoved(RemovedCallback removeConfig)
125     -> sdbusplus::async::task<void>
126 {
127     debug("setting up dbus match for interfaces removed");
128 
129     sdbusplus::async::match removedMatch(
130         ctx, rules_intf::interfacesRemoved() + senderRule);
131 
132     while (!ctx.stop_requested())
133     {
134         auto tmp = co_await removedMatch.next<sdbusplus::message::object_path,
135                                               std::vector<std::string>>();
136         auto [objectPath, interfaces] = std::move(tmp);
137 
138         if (!std::ranges::contains(interfaces, interface))
139         {
140             continue;
141         }
142 
143         debug("Detected interface {INTF} removed on {OBJPATH}", "INTF",
144               interface, "OBJPATH", objectPath);
145 
146         removeConfig(objectPath);
147     }
148 };
149 
150 } // namespace gpio_presence
151