blob: 9142c1837fc99376258ca7078a5c81a5a54b3374 [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
28static constexpr auto retryableErrors = {
29 /*
30 * Retry on bus or device errors or timeouts in case
31 * they are transient.
32 */
33 EIO,
34 ETIMEDOUT,
35
36 /*
37 * Retry CRC errors.
38 */
39 EBADMSG,
40
41 /*
42 * Some hwmon drivers do this when they aren't ready
43 * instead of blocking. Retry.
44 */
45 EAGAIN,
46 /*
47 * We'll see this when for example i2c devices are
48 * unplugged but the driver is still bound. Retry
49 * rather than exit on the off chance the device is
50 * plugged back in and the driver doesn't do a
51 * remove/probe. If a remove does occur, we'll
52 * eventually get ENOENT.
53 */
54 ENXIO,
55
56 /*
57 * Some devices return this when they are busy doing
58 * something else. Even if being busy isn't the cause,
59 * a retry still gives this app a shot at getting data
60 * as opposed to failing out on the first try.
61 */
62 ENODATA,
Eddie Jamese2891002018-06-01 10:21:48 -050063
64 /*
65 * Some devices return this if the hardware is being
66 * powered off in a normal manner, as incomplete data
67 * is received. Retrying allows time for the system to
68 * clean up driver devices, or in the event of a real
69 * failure, attempt to get the rest of the data.
70 */
71 EMSGSIZE,
Patrick Venture75e56c62018-04-20 18:10:15 -070072};
73
Patrick Venture4d9506c2018-12-19 14:19:34 -080074HwmonIO::HwmonIO(const std::string& path) : _p(path)
Patrick Venture75e56c62018-04-20 18:10:15 -070075{
76}
77
Patrick Venture043d3232018-08-31 10:10:53 -070078int64_t HwmonIO::read(const std::string& type, const std::string& id,
79 const std::string& sensor, size_t retries,
80 std::chrono::milliseconds delay) const
Patrick Venture75e56c62018-04-20 18:10:15 -070081{
82 int64_t val;
83 std::ifstream ifs;
Patrick Venture4d9506c2018-12-19 14:19:34 -080084 auto fullPath = sysfs::make_sysfs_path(_p, type, id, sensor);
Patrick Venture75e56c62018-04-20 18:10:15 -070085
Patrick Venture043d3232018-08-31 10:10:53 -070086 ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit |
87 std::ifstream::eofbit);
Patrick Venture75e56c62018-04-20 18:10:15 -070088
89 while (true)
90 {
91 try
92 {
93 errno = 0;
94 if (!ifs.is_open())
95 ifs.open(fullPath);
96 ifs.clear();
97 ifs.seekg(0);
98 ifs >> val;
99 }
100 catch (const std::exception& e)
101 {
102 auto rc = errno;
103
104 if (!rc)
105 {
106 throw;
107 }
108
109 if (rc == ENOENT || rc == ENODEV)
110 {
111 // If the directory or device disappeared then this application
Patrick Venture043d3232018-08-31 10:10:53 -0700112 // should gracefully exit. There are race conditions between
113 // the unloading of a hwmon driver and the stopping of this
114 // service by systemd. To prevent this application from falsely
115 // failing in these scenarios, it will simply exit if the
116 // directory or file can not be found. It is up to the user(s)
117 // of this provided hwmon object to log the appropriate errors
118 // if the object disappears when it should not.
Patrick Venture75e56c62018-04-20 18:10:15 -0700119 exit(0);
120 }
121
Patrick Venture043d3232018-08-31 10:10:53 -0700122 if (0 == std::count(retryableErrors.begin(), retryableErrors.end(),
123 rc) ||
124 !retries)
Patrick Venture75e56c62018-04-20 18:10:15 -0700125 {
126 // Not a retryable error or out of retries.
127#ifdef NEGATIVE_ERRNO_ON_FAIL
128 return -rc;
129#endif
130
131 // Work around GCC bugs 53984 and 66145 for callers by
132 // explicitly raising system_error here.
133 throw std::system_error(rc, std::generic_category());
134 }
135
136 --retries;
137 std::this_thread::sleep_for(delay);
138 continue;
139 }
140 break;
141 }
142
143 return val;
144}
145
Patrick Venture043d3232018-08-31 10:10:53 -0700146void HwmonIO::write(uint32_t val, const std::string& type,
147 const std::string& id, const std::string& sensor,
148 size_t retries, std::chrono::milliseconds delay) const
Patrick Venture75e56c62018-04-20 18:10:15 -0700149
150{
151 std::ofstream ofs;
Patrick Venture4d9506c2018-12-19 14:19:34 -0800152 auto fullPath = sysfs::make_sysfs_path(_p, type, id, sensor);
Patrick Venture75e56c62018-04-20 18:10:15 -0700153
Patrick Venture043d3232018-08-31 10:10:53 -0700154 ofs.exceptions(std::ofstream::failbit | std::ofstream::badbit |
155 std::ofstream::eofbit);
Patrick Venture75e56c62018-04-20 18:10:15 -0700156
157 // See comments in the read method for an explanation of the odd exception
158 // handling behavior here.
159
160 while (true)
161 {
162 try
163 {
164 errno = 0;
165 if (!ofs.is_open())
166 ofs.open(fullPath);
167 ofs.clear();
168 ofs.seekp(0);
169 ofs << val;
170 ofs.flush();
171 }
172 catch (const std::exception& e)
173 {
174 auto rc = errno;
175
176 if (!rc)
177 {
178 throw;
179 }
180
181 if (rc == ENOENT)
182 {
183 exit(0);
184 }
185
Patrick Venture043d3232018-08-31 10:10:53 -0700186 if (0 == std::count(retryableErrors.begin(), retryableErrors.end(),
187 rc) ||
188 !retries)
Patrick Venture75e56c62018-04-20 18:10:15 -0700189 {
190 // Not a retryable error or out of retries.
191
192 // Work around GCC bugs 53984 and 66145 for callers by
193 // explicitly raising system_error here.
194 throw std::system_error(rc, std::generic_category());
195 }
196
197 --retries;
198 std::this_thread::sleep_for(delay);
199 continue;
200 }
201 break;
202 }
203}
204
205std::string HwmonIO::path() const
206{
Patrick Venture4d9506c2018-12-19 14:19:34 -0800207 return _p;
Patrick Venture75e56c62018-04-20 18:10:15 -0700208}
209
Patrick Venture043d3232018-08-31 10:10:53 -0700210} // namespace hwmonio