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