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