1 #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"
11 namespace open_power
12 {
13 namespace occ
14 {
15 
16 // Value in error file indicating success
17 constexpr auto NO_ERROR = '0';
18 
19 using namespace phosphor::logging;
20 using namespace sdbusplus::org::open_power::OCC::Device::Error;
21 using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
22                                 Error::InternalFailure;
23 
24 // Populate the file descriptor on the error file
25 void 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
41 void 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
57 void Error::addWatch()
58 {
59     // Open the file
60     openFile();
61 
62     // register the callback handler
63     registerCallBack();
64 }
65 
66 // Stops watching for errors
67 void 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
81 int 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
92 void 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
124 std::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
146