blob: 851ad9de3aa3fded7429e69fd92444f1c005e702 [file] [log] [blame]
Patrick Venture75e56c62018-04-20 18:10:15 -07001/**
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 */
Patrick Venture043d3232018-08-31 10:10:53 -070014#include "config.h"
15
16#include "hwmonio.hpp"
17
18#include "sysfs.hpp"
19
Patrick Venture75e56c62018-04-20 18:10:15 -070020#include <algorithm>
21#include <exception>
22#include <fstream>
23#include <thread>
24
Patrick Venture043d3232018-08-31 10:10:53 -070025namespace hwmonio
26{
Patrick Venture75e56c62018-04-20 18:10:15 -070027
Patrick Venturecaaebd12019-06-21 14:56:07 -070028int64_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
45void 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
60FileSystem fileSystemImpl;
61
Patrick Venture75e56c62018-04-20 18:10:15 -070062static 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,
Eddie Jamese2891002018-06-01 10:21:48 -050097
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,
Patrick Venture75e56c62018-04-20 18:10:15 -0700106};
107
Patrick Venturecaaebd12019-06-21 14:56:07 -0700108HwmonIO::HwmonIO(const std::string& path, const FileSystemInterface* intf) :
109 _p(path), _intf(intf)
Patrick Williamse8771fd2023-05-10 07:51:06 -0500110{}
Patrick Venture75e56c62018-04-20 18:10:15 -0700111
Patrick Venture043d3232018-08-31 10:10:53 -0700112int64_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
Patrick Venture75e56c62018-04-20 18:10:15 -0700115{
116 int64_t val;
Patrick Venture4d9506c2018-12-19 14:19:34 -0800117 auto fullPath = sysfs::make_sysfs_path(_p, type, id, sensor);
Patrick Venture75e56c62018-04-20 18:10:15 -0700118
Patrick Venture75e56c62018-04-20 18:10:15 -0700119 while (true)
120 {
121 try
122 {
Patrick Venturecaaebd12019-06-21 14:56:07 -0700123 val = _intf->read(fullPath);
Patrick Venture75e56c62018-04-20 18:10:15 -0700124 }
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
Patrick Venture043d3232018-08-31 10:10:53 -0700137 // 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.
Patrick Venture75e56c62018-04-20 18:10:15 -0700144 exit(0);
145 }
146
Patrick Venture043d3232018-08-31 10:10:53 -0700147 if (0 == std::count(retryableErrors.begin(), retryableErrors.end(),
148 rc) ||
149 !retries)
Patrick Venture75e56c62018-04-20 18:10:15 -0700150 {
151 // Not a retryable error or out of retries.
Matt Spinlerd8cacfd2021-04-26 09:56:21 -0500152#if NEGATIVE_ERRNO_ON_FAIL
Patrick Venture75e56c62018-04-20 18:10:15 -0700153 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
Patrick Venture043d3232018-08-31 10:10:53 -0700171void 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
Patrick Venture75e56c62018-04-20 18:10:15 -0700174
175{
Patrick Venture4d9506c2018-12-19 14:19:34 -0800176 auto fullPath = sysfs::make_sysfs_path(_p, type, id, sensor);
Patrick Venture75e56c62018-04-20 18:10:15 -0700177
Patrick Venture75e56c62018-04-20 18:10:15 -0700178 // 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 {
Patrick Venturecaaebd12019-06-21 14:56:07 -0700185 _intf->write(fullPath, val);
Patrick Venture75e56c62018-04-20 18:10:15 -0700186 }
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
Patrick Venture043d3232018-08-31 10:10:53 -0700201 if (0 == std::count(retryableErrors.begin(), retryableErrors.end(),
202 rc) ||
203 !retries)
Patrick Venture75e56c62018-04-20 18:10:15 -0700204 {
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
220std::string HwmonIO::path() const
221{
Patrick Venture4d9506c2018-12-19 14:19:34 -0800222 return _p;
Patrick Venture75e56c62018-04-20 18:10:15 -0700223}
224
Patrick Venture043d3232018-08-31 10:10:53 -0700225} // namespace hwmonio