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