blob: f2da49fd17e4fb586c44e1f6c9e4a847ab53b3a6 [file] [log] [blame]
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +05301#include <fcntl.h>
2#include <unistd.h>
3#include <sys/ioctl.h>
4#include <errno.h>
5#include <phosphor-logging/log.hpp>
6#include <phosphor-logging/elog.hpp>
7#include <xyz/openbmc_project/Common/error.hpp>
8#include <org/open_power/OCC/Device/error.hpp>
9#include "occ_errors.hpp"
10#include "elog-errors.hpp"
11namespace open_power
12{
13namespace occ
14{
15
16// Value in error file indicating success
17constexpr auto NO_ERROR = '0';
18
19using namespace phosphor::logging;
20using namespace sdbusplus::org::open_power::OCC::Device::Error;
21using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
22 Error::InternalFailure;
23
24// Populate the file descriptor on the error file
25void Error::openFile()
26{
27 using namespace phosphor::logging;
28
29 fd = open(file.c_str(), O_RDONLY | O_NONBLOCK);
30 if (fd < 0)
31 {
32 elog<OpenFailure>(
33 phosphor::logging::org::open_power::OCC::Device::
34 OpenFailure::CALLOUT_ERRNO(errno),
35 phosphor::logging::org::open_power::OCC::Device::
36 OpenFailure::CALLOUT_DEVICE_PATH(file.c_str()));
37 }
38}
39
40// Attaches the FD to event loop and registers the callback handler
41void Error::registerCallBack()
42{
43 decltype(eventSource.get()) sourcePtr = nullptr;
44 auto r = sd_event_add_io(event.get(), &sourcePtr, fd,
45 EPOLLIN, processEvents, this);
46 eventSource.reset(sourcePtr);
47
48 if (r < 0)
49 {
50 log<level::ERR>("Failed to register callback handler",
51 entry("ERROR=%s", strerror(-r)));
52 elog<InternalFailure>();
53 }
54}
55
56// Starts to watch for errors
57void Error::addWatch()
58{
59 // Open the file
60 openFile();
61
62 // register the callback handler
63 registerCallBack();
64}
65
66// Stops watching for errors
67void Error::removeWatch()
68{
69 // Close the file
70 if (fd >= 0)
71 {
72 close(fd);
73 }
74
75 // Reduce the reference count. Since there is only one instances
76 // of add_io, this will result empty loop
77 eventSource.reset();
78}
79
80// Callback handler when there is an activity on the FD
81int Error::processEvents(sd_event_source* es, int fd,
82 uint32_t revents, void* userData)
83{
84 log<level::INFO>("Error file updated");
85 auto error = static_cast<Error*>(userData);
86
87 error->analyzeEvent();
88 return 0;
89}
90
91// Reads the error file and analyzes the data
92void Error::analyzeEvent()
93{
94 // Get the number of bytes to read
95 int len = -1;
96 auto r = ioctl(fd, FIONREAD, &len);
97 if (r < 0)
98 {
99 elog<ConfigFailure>(
100 phosphor::logging::org::open_power::OCC::Device::
101 ConfigFailure::CALLOUT_ERRNO(errno),
102 phosphor::logging::org::open_power::OCC::Device::
103 ConfigFailure::CALLOUT_DEVICE_PATH(file.c_str()));
104 }
105
106 // A non-zero data indicates an error condition
107 // Let the caller take appropriate action on this
108 auto data = readFile(len);
109 if (data.empty() ||
110 data.front() == NO_ERROR)
111 {
112 return;
113 }
114
115 // This must be an error
116 if (callBack)
117 {
118 callBack();
119 }
120 return;
121}
122
123// Reads so many bytes as passed in
124std::string Error::readFile(int len) const
125{
126 auto data = std::make_unique<char[]>(len+1);
127
128 // This file get created soon after binding. A value of 0 is
129 // deemed success and anything else is a Failure
130 // Since all the sysfs files would have size of 4096, if we read 0
131 // bytes -or- value '0', then it just means we are fine
132 auto r = read(fd, data.get(), len);
133 if (r < 0)
134 {
135 elog<ReadFailure>(
136 phosphor::logging::org::open_power::OCC::Device::
137 ReadFailure::CALLOUT_ERRNO(errno),
138 phosphor::logging::org::open_power::OCC::Device::
139 ReadFailure::CALLOUT_DEVICE_PATH(file.c_str()));
140 }
141 return std::string(data.get());
142}
143
144} // namespace occ
145} // namespace open_power