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 113 int64_t HwmonIO::read(const std::string& type, const std::string& id, 114 const std::string& sensor, size_t retries, 115 std::chrono::milliseconds delay) const 116 { 117 int64_t val; 118 auto fullPath = sysfs::make_sysfs_path(_p, type, id, sensor); 119 120 while (true) 121 { 122 try 123 { 124 val = _intf->read(fullPath); 125 } 126 catch (const std::exception& e) 127 { 128 auto rc = errno; 129 130 if (!rc) 131 { 132 throw; 133 } 134 135 if (rc == ENOENT || rc == ENODEV) 136 { 137 // If the directory or device disappeared then this application 138 // should gracefully exit. There are race conditions between 139 // the unloading of a hwmon driver and the stopping of this 140 // service by systemd. To prevent this application from falsely 141 // failing in these scenarios, it will simply exit if the 142 // directory or file can not be found. It is up to the user(s) 143 // of this provided hwmon object to log the appropriate errors 144 // if the object disappears when it should not. 145 exit(0); 146 } 147 148 if (0 == std::count(retryableErrors.begin(), retryableErrors.end(), 149 rc) || 150 !retries) 151 { 152 // Not a retryable error or out of retries. 153 #ifdef NEGATIVE_ERRNO_ON_FAIL 154 return -rc; 155 #endif 156 157 // Work around GCC bugs 53984 and 66145 for callers by 158 // explicitly raising system_error here. 159 throw std::system_error(rc, std::generic_category()); 160 } 161 162 --retries; 163 std::this_thread::sleep_for(delay); 164 continue; 165 } 166 break; 167 } 168 169 return val; 170 } 171 172 void HwmonIO::write(uint32_t val, const std::string& type, 173 const std::string& id, const std::string& sensor, 174 size_t retries, std::chrono::milliseconds delay) const 175 176 { 177 auto fullPath = sysfs::make_sysfs_path(_p, type, id, sensor); 178 179 // See comments in the read method for an explanation of the odd exception 180 // handling behavior here. 181 182 while (true) 183 { 184 try 185 { 186 _intf->write(fullPath, val); 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(retryableErrors.begin(), retryableErrors.end(), 203 rc) || 204 !retries) 205 { 206 // Not a retryable error or out of retries. 207 208 // Work around GCC bugs 53984 and 66145 for callers by 209 // explicitly raising system_error here. 210 throw std::system_error(rc, std::generic_category()); 211 } 212 213 --retries; 214 std::this_thread::sleep_for(delay); 215 continue; 216 } 217 break; 218 } 219 } 220 221 std::string HwmonIO::path() const 222 { 223 return _p; 224 } 225 226 } // namespace hwmonio 227