blob: fe167f5d0fd06ed8b248a9b431fef0b0b56b031b [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,
60};
61
62HwmonIO::HwmonIO(const std::string& path) : p(path)
63{
64}
65
66int64_t HwmonIO::read(
67 const std::string& type,
68 const std::string& id,
69 const std::string& sensor,
70 size_t retries,
71 std::chrono::milliseconds delay,
72 bool isOCC) const
73{
74 int64_t val;
75 std::ifstream ifs;
76 auto fullPath = sysfs::make_sysfs_path(
77 p, type, id, sensor);
78
79 ifs.exceptions(
80 std::ifstream::failbit |
81 std::ifstream::badbit |
82 std::ifstream::eofbit);
83
84 while (true)
85 {
86 try
87 {
88 errno = 0;
89 if (!ifs.is_open())
90 ifs.open(fullPath);
91 ifs.clear();
92 ifs.seekg(0);
93 ifs >> val;
94 }
95 catch (const std::exception& e)
96 {
97 auto rc = errno;
98
99 if (!rc)
100 {
101 throw;
102 }
103
104 if (rc == ENOENT || rc == ENODEV)
105 {
106 // If the directory or device disappeared then this application
107 // should gracefully exit. There are race conditions between the
108 // unloading of a hwmon driver and the stopping of this service
109 // by systemd. To prevent this application from falsely failing
110 // in these scenarios, it will simply exit if the directory or
111 // file can not be found. It is up to the user(s) of this
112 // provided hwmon object to log the appropriate errors if the
113 // object disappears when it should not.
114 exit(0);
115 }
116
117 if (isOCC)
118 {
119 if (rc == EREMOTEIO)
120 {
121 // For the OCCs, when an EREMOTEIO is return, set the
122 // value to 255*1000
123 // (0xFF = sensor is failed, 1000 = sensor factor)
124 val = 255000;
125 break;
126 }
127 }
128
129 if (0 == std::count(
130 retryableErrors.begin(),
131 retryableErrors.end(),
132 rc) ||
133 !retries)
134 {
135 // Not a retryable error or out of retries.
136#ifdef NEGATIVE_ERRNO_ON_FAIL
137 return -rc;
138#endif
139
140 // Work around GCC bugs 53984 and 66145 for callers by
141 // explicitly raising system_error here.
142 throw std::system_error(rc, std::generic_category());
143 }
144
145 --retries;
146 std::this_thread::sleep_for(delay);
147 continue;
148 }
149 break;
150 }
151
152 return val;
153}
154
155void HwmonIO::write(
156 uint32_t val,
157 const std::string& type,
158 const std::string& id,
159 const std::string& sensor,
160 size_t retries,
161 std::chrono::milliseconds delay) const
162
163{
164 std::ofstream ofs;
165 auto fullPath = sysfs::make_sysfs_path(
166 p, type, id, sensor);
167
168 ofs.exceptions(
169 std::ofstream::failbit |
170 std::ofstream::badbit |
171 std::ofstream::eofbit);
172
173 // See comments in the read method for an explanation of the odd exception
174 // handling behavior here.
175
176 while (true)
177 {
178 try
179 {
180 errno = 0;
181 if (!ofs.is_open())
182 ofs.open(fullPath);
183 ofs.clear();
184 ofs.seekp(0);
185 ofs << val;
186 ofs.flush();
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(
203 retryableErrors.begin(),
204 retryableErrors.end(),
205 rc) ||
206 !retries)
207 {
208 // Not a retryable error or out of retries.
209
210 // Work around GCC bugs 53984 and 66145 for callers by
211 // explicitly raising system_error here.
212 throw std::system_error(rc, std::generic_category());
213 }
214
215 --retries;
216 std::this_thread::sleep_for(delay);
217 continue;
218 }
219 break;
220 }
221}
222
223std::string HwmonIO::path() const
224{
225 return p;
226}
227
228} // hwmonio