xref: /openbmc/dbus-sensors/src/PSUEvent.cpp (revision bb67932a)
1 /*
2 // Copyright (c) 2019 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 
17 #include <PSUEvent.hpp>
18 #include <SensorPaths.hpp>
19 #include <boost/asio/io_service.hpp>
20 #include <boost/asio/read_until.hpp>
21 #include <boost/container/flat_map.hpp>
22 #include <phosphor-logging/lg2.hpp>
23 #include <sdbusplus/asio/connection.hpp>
24 #include <sdbusplus/asio/object_server.hpp>
25 
26 #include <iostream>
27 #include <memory>
28 #include <stdexcept>
29 #include <string>
30 #include <utility>
31 #include <variant>
32 #include <vector>
33 
34 PSUCombineEvent::PSUCombineEvent(
35     sdbusplus::asio::object_server& objectServer,
36     std::shared_ptr<sdbusplus::asio::connection>& conn,
37     boost::asio::io_service& io, const std::string& psuName,
38     const PowerState& powerState,
39     boost::container::flat_map<std::string, std::vector<std::string>>&
40         eventPathList,
41     boost::container::flat_map<
42         std::string,
43         boost::container::flat_map<std::string, std::vector<std::string>>>&
44         groupEventPathList,
45     const std::string& combineEventName, double pollRate) :
46     objServer(objectServer)
47 {
48     std::string psuNameEscaped = sensor_paths::escapePathForDbus(psuName);
49     eventInterface = objServer.add_interface(
50         "/xyz/openbmc_project/State/Decorator/" + psuNameEscaped + "_" +
51             combineEventName,
52         "xyz.openbmc_project.State.Decorator.OperationalStatus");
53     eventInterface->register_property("functional", true);
54 
55     if (!eventInterface->initialize())
56     {
57         std::cerr << "error initializing event interface\n";
58     }
59 
60     std::shared_ptr<std::set<std::string>> combineEvent =
61         std::make_shared<std::set<std::string>>();
62     for (const auto& pathList : eventPathList)
63     {
64         std::shared_ptr<std::set<std::string>> assert =
65             std::make_shared<std::set<std::string>>();
66         std::shared_ptr<bool> state = std::make_shared<bool>(false);
67 
68         const std::string& eventName = pathList.first;
69         std::string eventPSUName = eventName + psuName;
70         for (const auto& path : pathList.second)
71         {
72             auto p = std::make_shared<PSUSubEvent>(
73                 eventInterface, path, conn, io, powerState, eventName,
74                 eventName, assert, combineEvent, state, psuName, pollRate);
75             p->setupRead();
76 
77             events[eventPSUName].emplace_back(p);
78             asserts.emplace_back(assert);
79             states.emplace_back(state);
80         }
81     }
82 
83     for (const auto& groupPathList : groupEventPathList)
84     {
85         for (const auto& pathList : groupPathList.second)
86         {
87             std::shared_ptr<std::set<std::string>> assert =
88                 std::make_shared<std::set<std::string>>();
89             std::shared_ptr<bool> state = std::make_shared<bool>(false);
90 
91             const std::string& groupEventName = pathList.first;
92             std::string eventPSUName = groupEventName + psuName;
93             for (const auto& path : pathList.second)
94             {
95                 auto p = std::make_shared<PSUSubEvent>(
96                     eventInterface, path, conn, io, powerState, groupEventName,
97                     groupPathList.first, assert, combineEvent, state, psuName,
98                     pollRate);
99                 p->setupRead();
100                 events[eventPSUName].emplace_back(p);
101 
102                 asserts.emplace_back(assert);
103                 states.emplace_back(state);
104             }
105         }
106     }
107 }
108 
109 PSUCombineEvent::~PSUCombineEvent()
110 {
111     // Clear unique_ptr first
112     for (auto& event : events)
113     {
114         for (auto& subEventPtr : event.second)
115         {
116             subEventPtr.reset();
117         }
118     }
119     events.clear();
120     objServer.remove_interface(eventInterface);
121 }
122 
123 static boost::container::flat_map<std::string,
124                                   std::pair<std::string, std::string>>
125     logID = {
126         {"PredictiveFailure",
127          {"OpenBMC.0.1.PowerSupplyFailurePredicted",
128           "OpenBMC.0.1.PowerSupplyPredictedFailureRecovered"}},
129         {"Failure",
130          {"OpenBMC.0.1.PowerSupplyFailed", "OpenBMC.0.1.PowerSupplyRecovered"}},
131         {"ACLost",
132          {"OpenBMC.0.1.PowerSupplyPowerLost",
133           "OpenBMC.0.1.PowerSupplyPowerRestored"}},
134         {"FanFault",
135          {"OpenBMC.0.1.PowerSupplyFanFailed",
136           "OpenBMC.0.1.PowerSupplyFanRecovered"}},
137         {"ConfigureError",
138          {"OpenBMC.0.1.PowerSupplyConfigurationError",
139           "OpenBMC.0.1.PowerSupplyConfigurationErrorRecovered"}}};
140 
141 PSUSubEvent::PSUSubEvent(
142     std::shared_ptr<sdbusplus::asio::dbus_interface> eventInterface,
143     const std::string& path, std::shared_ptr<sdbusplus::asio::connection>& conn,
144     boost::asio::io_service& io, const PowerState& powerState,
145     const std::string& groupEventName, const std::string& eventName,
146     std::shared_ptr<std::set<std::string>> asserts,
147     std::shared_ptr<std::set<std::string>> combineEvent,
148     std::shared_ptr<bool> state, const std::string& psuName, double pollRate) :
149     std::enable_shared_from_this<PSUSubEvent>(),
150     eventInterface(std::move(eventInterface)), asserts(std::move(asserts)),
151     combineEvent(std::move(combineEvent)), assertState(std::move(state)),
152     errCount(0), path(path), eventName(eventName), readState(powerState),
153     waitTimer(io), inputDev(io), psuName(psuName),
154     groupEventName(groupEventName), systemBus(conn)
155 {
156     if (pollRate > 0.0)
157     {
158         eventPollMs = static_cast<unsigned int>(pollRate * 1000);
159     }
160 
161     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
162     fd = open(path.c_str(), O_RDONLY);
163     if (fd < 0)
164     {
165         std::cerr << "PSU sub event failed to open file\n";
166         return;
167     }
168     inputDev.assign(fd);
169 
170     auto found = logID.find(eventName);
171     if (found == logID.end())
172     {
173         assertMessage.clear();
174         deassertMessage.clear();
175     }
176     else
177     {
178         assertMessage = found->second.first;
179         deassertMessage = found->second.second;
180     }
181 
182     auto fanPos = path.find("fan");
183     if (fanPos != std::string::npos)
184     {
185         fanName = path.substr(fanPos);
186         auto fanNamePos = fanName.find('_');
187         if (fanNamePos != std::string::npos)
188         {
189             fanName = fanName.substr(0, fanNamePos);
190         }
191     }
192 }
193 
194 PSUSubEvent::~PSUSubEvent()
195 {
196     waitTimer.cancel();
197     inputDev.close();
198 }
199 
200 void PSUSubEvent::setupRead(void)
201 {
202     if (!readingStateGood(readState))
203     {
204         // Deassert the event
205         updateValue(0);
206         restartRead();
207         return;
208     }
209 
210     std::shared_ptr<boost::asio::streambuf> buffer =
211         std::make_shared<boost::asio::streambuf>();
212     std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
213     boost::asio::async_read_until(
214         inputDev, *buffer, '\n',
215         [weakRef, buffer](const boost::system::error_code& ec,
216                           std::size_t /*bytes_transfered*/) {
217         std::shared_ptr<PSUSubEvent> self = weakRef.lock();
218         if (self)
219         {
220             self->readBuf = buffer;
221             self->handleResponse(ec);
222         }
223         });
224 }
225 
226 void PSUSubEvent::restartRead()
227 {
228     std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
229     waitTimer.expires_from_now(boost::posix_time::milliseconds(eventPollMs));
230     waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
231         if (ec == boost::asio::error::operation_aborted)
232         {
233             return;
234         }
235         std::shared_ptr<PSUSubEvent> self = weakRef.lock();
236         if (self)
237         {
238             self->setupRead();
239         }
240     });
241 }
242 
243 void PSUSubEvent::handleResponse(const boost::system::error_code& err)
244 {
245     if ((err == boost::system::errc::bad_file_descriptor) ||
246         (err == boost::asio::error::misc_errors::not_found))
247     {
248         return;
249     }
250     std::istream responseStream(readBuf.get());
251     if (!err)
252     {
253         std::string response;
254         try
255         {
256             std::getline(responseStream, response);
257             int nvalue = std::stoi(response);
258             responseStream.clear();
259 
260             updateValue(nvalue);
261             errCount = 0;
262         }
263         catch (const std::invalid_argument&)
264         {
265             errCount++;
266         }
267     }
268     else
269     {
270         errCount++;
271     }
272     if (errCount >= warnAfterErrorCount)
273     {
274         if (errCount == warnAfterErrorCount)
275         {
276             std::cerr << "Failure to read event at " << path << "\n";
277         }
278         updateValue(0);
279         errCount++;
280     }
281     lseek(fd, 0, SEEK_SET);
282     restartRead();
283 }
284 
285 // Any of the sub events of one event is asserted, then the event will be
286 // asserted. Only if none of the sub events are asserted, the event will be
287 // deasserted.
288 void PSUSubEvent::updateValue(const int& newValue)
289 {
290     // Take no action if value already equal
291     // Same semantics as Sensor::updateValue(const double&)
292     if (newValue == value)
293     {
294         return;
295     }
296 
297     if (newValue == 0)
298     {
299         // log deassert only after all asserts are gone
300         if (!(*asserts).empty())
301         {
302             auto found = (*asserts).find(path);
303             if (found == (*asserts).end())
304             {
305                 return;
306             }
307             (*asserts).erase(path);
308 
309             return;
310         }
311         if (*assertState == true)
312         {
313             *assertState = false;
314             auto foundCombine = (*combineEvent).find(groupEventName);
315             if (foundCombine == (*combineEvent).end())
316             {
317                 return;
318             }
319             (*combineEvent).erase(groupEventName);
320             if (!deassertMessage.empty())
321             {
322                 // Fan Failed has two args
323                 if (deassertMessage == "OpenBMC.0.1.PowerSupplyFanRecovered")
324                 {
325                     lg2::info("{EVENT} deassert", "EVENT", eventName,
326                               "REDFISH_MESSAGE_ID", deassertMessage,
327                               "REDFISH_MESSAGE_ARGS",
328                               (psuName + ',' + fanName));
329                 }
330                 else
331                 {
332                     lg2::info("{EVENT} deassert", "EVENT", eventName,
333                               "REDFISH_MESSAGE_ID", deassertMessage,
334                               "REDFISH_MESSAGE_ARGS", psuName);
335                 }
336             }
337 
338             if ((*combineEvent).empty())
339             {
340                 eventInterface->set_property("functional", true);
341             }
342         }
343     }
344     else
345     {
346         std::cerr << "PSUSubEvent asserted by " << path << "\n";
347 
348         if ((*assertState == false) && ((*asserts).empty()))
349         {
350             *assertState = true;
351             if (!assertMessage.empty())
352             {
353                 // Fan Failed has two args
354                 if (assertMessage == "OpenBMC.0.1.PowerSupplyFanFailed")
355                 {
356                     lg2::warning("{EVENT} assert", "EVENT", eventName,
357                                  "REDFISH_MESSAGE_ID", assertMessage,
358                                  "REDFISH_MESSAGE_ARGS",
359                                  (psuName + ',' + fanName));
360                 }
361                 else
362                 {
363                     lg2::warning("{EVENT} assert", "EVENT", eventName,
364                                  "REDFISH_MESSAGE_ID", assertMessage,
365                                  "REDFISH_MESSAGE_ARGS", psuName);
366                 }
367             }
368             if ((*combineEvent).empty())
369             {
370                 eventInterface->set_property("functional", false);
371             }
372             (*combineEvent).emplace(groupEventName);
373         }
374         (*asserts).emplace(path);
375     }
376     value = newValue;
377 }
378