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) const 72 { 73 int64_t val; 74 std::ifstream ifs; 75 auto fullPath = sysfs::make_sysfs_path( 76 p, type, id, sensor); 77 78 ifs.exceptions( 79 std::ifstream::failbit | 80 std::ifstream::badbit | 81 std::ifstream::eofbit); 82 83 while (true) 84 { 85 try 86 { 87 errno = 0; 88 if (!ifs.is_open()) 89 ifs.open(fullPath); 90 ifs.clear(); 91 ifs.seekg(0); 92 ifs >> val; 93 } 94 catch (const std::exception& e) 95 { 96 auto rc = errno; 97 98 if (!rc) 99 { 100 throw; 101 } 102 103 if (rc == ENOENT || rc == ENODEV) 104 { 105 // If the directory or device disappeared then this application 106 // should gracefully exit. There are race conditions between the 107 // unloading of a hwmon driver and the stopping of this service 108 // by systemd. To prevent this application from falsely failing 109 // in these scenarios, it will simply exit if the directory or 110 // file can not be found. It is up to the user(s) of this 111 // provided hwmon object to log the appropriate errors if the 112 // object disappears when it should not. 113 exit(0); 114 } 115 116 if (0 == std::count( 117 retryableErrors.begin(), 118 retryableErrors.end(), 119 rc) || 120 !retries) 121 { 122 // Not a retryable error or out of retries. 123 #ifdef NEGATIVE_ERRNO_ON_FAIL 124 return -rc; 125 #endif 126 127 // Work around GCC bugs 53984 and 66145 for callers by 128 // explicitly raising system_error here. 129 throw std::system_error(rc, std::generic_category()); 130 } 131 132 --retries; 133 std::this_thread::sleep_for(delay); 134 continue; 135 } 136 break; 137 } 138 139 return val; 140 } 141 142 void HwmonIO::write( 143 uint32_t val, 144 const std::string& type, 145 const std::string& id, 146 const std::string& sensor, 147 size_t retries, 148 std::chrono::milliseconds delay) const 149 150 { 151 std::ofstream ofs; 152 auto fullPath = sysfs::make_sysfs_path( 153 p, type, id, sensor); 154 155 ofs.exceptions( 156 std::ofstream::failbit | 157 std::ofstream::badbit | 158 std::ofstream::eofbit); 159 160 // See comments in the read method for an explanation of the odd exception 161 // handling behavior here. 162 163 while (true) 164 { 165 try 166 { 167 errno = 0; 168 if (!ofs.is_open()) 169 ofs.open(fullPath); 170 ofs.clear(); 171 ofs.seekp(0); 172 ofs << val; 173 ofs.flush(); 174 } 175 catch (const std::exception& e) 176 { 177 auto rc = errno; 178 179 if (!rc) 180 { 181 throw; 182 } 183 184 if (rc == ENOENT) 185 { 186 exit(0); 187 } 188 189 if (0 == std::count( 190 retryableErrors.begin(), 191 retryableErrors.end(), 192 rc) || 193 !retries) 194 { 195 // Not a retryable error or out of retries. 196 197 // Work around GCC bugs 53984 and 66145 for callers by 198 // explicitly raising system_error here. 199 throw std::system_error(rc, std::generic_category()); 200 } 201 202 --retries; 203 std::this_thread::sleep_for(delay); 204 continue; 205 } 206 break; 207 } 208 } 209 210 std::string HwmonIO::path() const 211 { 212 return p; 213 } 214 215 } // hwmonio 216