blob: 97990ceb7e48db64472b5ca94a4593b2ddb71f1a [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 */
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
23namespace hwmonio {
24
25static 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,
Eddie Jamese2891002018-06-01 10:21:48 -050060
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,
Patrick Venture75e56c62018-04-20 18:10:15 -070069};
70
71HwmonIO::HwmonIO(const std::string& path) : p(path)
72{
73}
74
75int64_t HwmonIO::read(
76 const std::string& type,
77 const std::string& id,
78 const std::string& sensor,
79 size_t retries,
Matthew Barth0b305052018-04-26 09:46:49 -050080 std::chrono::milliseconds delay) const
Patrick Venture75e56c62018-04-20 18:10:15 -070081{
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
Patrick Venture75e56c62018-04-20 18:10:15 -0700125 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
151void 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
219std::string HwmonIO::path() const
220{
221 return p;
222}
223
224} // hwmonio