blob: 2812e7f68858020f87fe2ff8605e43617e1d7a15 [file] [log] [blame]
Jagpal Singh Gillca8c7e92024-11-02 16:51:48 -07001#include "CableMonitor.hpp"
2
3#include "CableConfig.hpp"
4
5#include <phosphor-logging/lg2.hpp>
6#include <sdbusplus/async.hpp>
Jagpal Singh Gillca8c7e92024-11-02 16:51:48 -07007#include <sdbusplus/message/native_types.hpp>
8#include <sdbusplus/server/manager.hpp>
9
10#include <chrono>
11#include <cstring>
12#include <filesystem>
13#include <functional>
14#include <string>
15
16PHOSPHOR_LOG2_USING;
17
18namespace cable
19{
20
21Monitor::Monitor(sdbusplus::async::context& ctx) :
22 ctx(ctx), cableEvents(ctx),
23 entityManager(ctx, {CableInventoryIntf::interface},
24 std::bind_front(&Monitor::inventoryAddedHandler, this),
25 std::bind_front(&Monitor::inventoryRemovedHandler, this)),
26 notifyWatch(ctx, Config::configFileDir,
27 std::bind_front(&Monitor::configUpdateHandler, this))
28{
29 ctx.spawn(start());
30}
31
32auto Monitor::inventoryAddedHandler(
33 const sdbusplus::message::object_path& objectPath,
34 const std::string& /*unused*/) -> void
35{
36 debug("Received cable added for {NAME}", "NAME", objectPath);
37 ctx.spawn(processCableAddedAsync(objectPath));
38}
39
40auto Monitor::inventoryRemovedHandler(
41 const sdbusplus::message::object_path& objectPath,
42 const std::string& /*unused*/) -> void
43{
44 debug("Received cable removed for {NAME}", "NAME", objectPath);
45 ctx.spawn(processCableRemovedAsync(objectPath));
46}
47
48auto Monitor::configUpdateHandler(std::string configFileName)
49 -> sdbusplus::async::task<>
50{
51 if (strcmp(Config::configFileName, configFileName.c_str()) != 0)
52 {
53 error("Update config file name {NAME} is not expected", "NAME",
54 configFileName);
55 co_return;
56 }
57 auto configFilePath =
58 std::string(Config::configFileDir) + "/" + configFileName;
59 if (!std::filesystem::exists(configFilePath))
60 {
61 error("Config file {NAME} does not exist", "NAME", configFilePath);
62 co_return;
63 }
64 expectedCables = co_await Config::processConfig(configFilePath);
65 if (expectedCables.empty())
66 {
67 error("No expected cables found in config file {NAME}", "NAME",
68 configFileName);
69 co_return;
70 }
71 co_await entityManager.handleInventoryGet();
Patrick Williams8c1235d2025-11-08 21:56:27 -050072 ctx.spawn([](auto& self) -> sdbusplus::async::task<> {
73 co_await sdbusplus::async::sleep_for(self.ctx, std::chrono::seconds(5));
74 self.reconcileCableData();
75 }(*this));
Jagpal Singh Gillca8c7e92024-11-02 16:51:48 -070076}
77
78auto Monitor::start() -> sdbusplus::async::task<>
79{
80 info("Start cable monitor");
81
82 // Start async handler for cable config update
83 ctx.spawn(notifyWatch.readNotifyAsync());
84
85 // Process the cable config if it already exists
86 auto configFilePath =
87 std::string(Config::configFileDir) + "/" + Config::configFileName;
88 if (std::filesystem::exists(configFilePath))
89 {
90 co_await configUpdateHandler(Config::configFileName);
91 }
92
93 co_return;
94}
95
96auto Monitor::processCableAddedAsync(sdbusplus::message::object_path objectPath)
97 -> sdbusplus::async::task<>
98{
99 auto cableName = objectPath.filename();
100
101 debug("Received cable added for {NAME}", "NAME", cableName);
102
103 if (connectedCables.contains(cableName))
104 {
105 debug("Cable {NAME} is already connected, so skip it", "NAME",
106 cableName);
107 co_return;
108 }
109 else if (expectedCables.empty())
110 {
111 debug("No expected cables yet, so skip cable add for {NAME}", "NAME",
112 cableName);
113 co_return;
114 }
115 else if (!expectedCables.contains(cableName))
116 {
117 debug(
118 "Cable {NAME} is not in expected cables, skip connected event generation",
119 "NAME", cableName);
120 co_return;
121 }
122
123 connectedCables.insert(cableName);
124 co_await cableEvents.generateCableEvent(Events::Type::connected, cableName);
125 debug("New cable {NAME} added", "NAME", cableName);
126
127 co_return;
128}
129
130auto Monitor::processCableRemovedAsync(
131 sdbusplus::message::object_path objectPath) -> sdbusplus::async::task<>
132{
133 auto cableName = objectPath.filename();
134
135 debug("Received cable removed for {NAME}", "NAME", cableName);
136
137 if (expectedCables.empty())
138 {
139 debug("No expected cables yet, so skip cable add for {NAME}", "NAME",
140 cableName);
141 co_return;
142 }
143 else if (!expectedCables.contains(cableName))
144 {
145 debug(
146 "Cable {NAME} is not in expected cables, so skip disconnected event generation",
147 "NAME", cableName);
148 co_return;
149 }
150 else if (!connectedCables.contains(cableName))
151 {
152 debug(
153 "Cable {NAME} is not connected, so skip disconnected event generation",
154 "NAME", cableName);
155 co_return;
156 }
157
158 connectedCables.erase(cableName);
159 co_await cableEvents.generateCableEvent(Events::Type::disconnected,
160 cableName);
161 debug("Removed cable {NAME}", "NAME", cableName);
162
163 co_return;
164}
165
166auto Monitor::reconcileCableData() -> void
167{
168 for (const auto& cableName : expectedCables)
169 {
170 if (connectedCables.contains(cableName))
171 {
172 continue;
173 }
174 ctx.spawn(cableEvents.generateCableEvent(Events::Type::disconnected,
175 cableName));
176 }
177}
178
179} // namespace cable
180
181int main()
182{
183 constexpr auto path = "/xyz/openbmc_project/cable_monitor";
184 constexpr auto serviceName = "xyz.openbmc_project.cablemonitor";
185 sdbusplus::async::context ctx;
186 sdbusplus::server::manager_t manager{ctx, path};
187
188 info("Creating cable monitor");
189 cable::Monitor cableMonitor{ctx};
190 ctx.request_name(serviceName);
191
192 ctx.run();
193 return 0;
194}