1 /** 2 * Licensed under the Apache License, Version 2.0 (the "License"); 3 * you may not use this file except in compliance with the License. 4 * You may obtain a copy of the License at 5 * 6 * http://www.apache.org/licenses/LICENSE-2.0 7 * 8 * Unless required by applicable law or agreed to in writing, software 9 * distributed under the License is distributed on an "AS IS" BASIS, 10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 * See the License for the specific language governing permissions and 12 * limitations under the License. 13 */ 14 #include <algorithm> 15 #include <exception> 16 #include <fstream> 17 #include <thread> 18 19 #include "config.h" 20 #include "hwmonio.hpp" 21 #include "sysfs.hpp" 22 23 namespace hwmonio { 24 25 static constexpr auto retryableErrors = { 26 /* 27 * Retry on bus or device errors or timeouts in case 28 * they are transient. 29 */ 30 EIO, 31 ETIMEDOUT, 32 33 /* 34 * Retry CRC errors. 35 */ 36 EBADMSG, 37 38 /* 39 * Some hwmon drivers do this when they aren't ready 40 * instead of blocking. Retry. 41 */ 42 EAGAIN, 43 /* 44 * We'll see this when for example i2c devices are 45 * unplugged but the driver is still bound. Retry 46 * rather than exit on the off chance the device is 47 * plugged back in and the driver doesn't do a 48 * remove/probe. If a remove does occur, we'll 49 * eventually get ENOENT. 50 */ 51 ENXIO, 52 53 /* 54 * Some devices return this when they are busy doing 55 * something else. Even if being busy isn't the cause, 56 * a retry still gives this app a shot at getting data 57 * as opposed to failing out on the first try. 58 */ 59 ENODATA, 60 }; 61 62 HwmonIO::HwmonIO(const std::string& path) : p(path) 63 { 64 } 65 66 int64_t HwmonIO::read( 67 const std::string& type, 68 const std::string& id, 69 const std::string& sensor, 70 size_t retries, 71 std::chrono::milliseconds delay, 72 bool isOCC) const 73 { 74 int64_t val; 75 std::ifstream ifs; 76 auto fullPath = sysfs::make_sysfs_path( 77 p, type, id, sensor); 78 79 ifs.exceptions( 80 std::ifstream::failbit | 81 std::ifstream::badbit | 82 std::ifstream::eofbit); 83 84 while (true) 85 { 86 try 87 { 88 errno = 0; 89 if (!ifs.is_open()) 90 ifs.open(fullPath); 91 ifs.clear(); 92 ifs.seekg(0); 93 ifs >> val; 94 } 95 catch (const std::exception& e) 96 { 97 auto rc = errno; 98 99 if (!rc) 100 { 101 throw; 102 } 103 104 if (rc == ENOENT || rc == ENODEV) 105 { 106 // If the directory or device disappeared then this application 107 // should gracefully exit. There are race conditions between the 108 // unloading of a hwmon driver and the stopping of this service 109 // by systemd. To prevent this application from falsely failing 110 // in these scenarios, it will simply exit if the directory or 111 // file can not be found. It is up to the user(s) of this 112 // provided hwmon object to log the appropriate errors if the 113 // object disappears when it should not. 114 exit(0); 115 } 116 117 if (isOCC) 118 { 119 if (rc == EREMOTEIO) 120 { 121 // For the OCCs, when an EREMOTEIO is return, set the 122 // value to 255*1000 123 // (0xFF = sensor is failed, 1000 = sensor factor) 124 val = 255000; 125 break; 126 } 127 } 128 129 if (0 == std::count( 130 retryableErrors.begin(), 131 retryableErrors.end(), 132 rc) || 133 !retries) 134 { 135 // Not a retryable error or out of retries. 136 #ifdef NEGATIVE_ERRNO_ON_FAIL 137 return -rc; 138 #endif 139 140 // Work around GCC bugs 53984 and 66145 for callers by 141 // explicitly raising system_error here. 142 throw std::system_error(rc, std::generic_category()); 143 } 144 145 --retries; 146 std::this_thread::sleep_for(delay); 147 continue; 148 } 149 break; 150 } 151 152 return val; 153 } 154 155 void HwmonIO::write( 156 uint32_t val, 157 const std::string& type, 158 const std::string& id, 159 const std::string& sensor, 160 size_t retries, 161 std::chrono::milliseconds delay) const 162 163 { 164 std::ofstream ofs; 165 auto fullPath = sysfs::make_sysfs_path( 166 p, type, id, sensor); 167 168 ofs.exceptions( 169 std::ofstream::failbit | 170 std::ofstream::badbit | 171 std::ofstream::eofbit); 172 173 // See comments in the read method for an explanation of the odd exception 174 // handling behavior here. 175 176 while (true) 177 { 178 try 179 { 180 errno = 0; 181 if (!ofs.is_open()) 182 ofs.open(fullPath); 183 ofs.clear(); 184 ofs.seekp(0); 185 ofs << val; 186 ofs.flush(); 187 } 188 catch (const std::exception& e) 189 { 190 auto rc = errno; 191 192 if (!rc) 193 { 194 throw; 195 } 196 197 if (rc == ENOENT) 198 { 199 exit(0); 200 } 201 202 if (0 == std::count( 203 retryableErrors.begin(), 204 retryableErrors.end(), 205 rc) || 206 !retries) 207 { 208 // Not a retryable error or out of retries. 209 210 // Work around GCC bugs 53984 and 66145 for callers by 211 // explicitly raising system_error here. 212 throw std::system_error(rc, std::generic_category()); 213 } 214 215 --retries; 216 std::this_thread::sleep_for(delay); 217 continue; 218 } 219 break; 220 } 221 } 222 223 std::string HwmonIO::path() const 224 { 225 return p; 226 } 227 228 } // hwmonio 229