blob: 18ceff9c87806b4b9d9684ef33d35f5baecdef08 [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,
Matthew Barth0b305052018-04-26 09:46:49 -050071 std::chrono::milliseconds delay) const
Patrick Venture75e56c62018-04-20 18:10:15 -070072{
73 int64_t val;
74 std::ifstream ifs;
75 auto fullPath = sysfs::make_sysfs_path(
76 p, type, id, sensor);
77
78 ifs.exceptions(
79 std::ifstream::failbit |
80 std::ifstream::badbit |
81 std::ifstream::eofbit);
82
83 while (true)
84 {
85 try
86 {
87 errno = 0;
88 if (!ifs.is_open())
89 ifs.open(fullPath);
90 ifs.clear();
91 ifs.seekg(0);
92 ifs >> val;
93 }
94 catch (const std::exception& e)
95 {
96 auto rc = errno;
97
98 if (!rc)
99 {
100 throw;
101 }
102
103 if (rc == ENOENT || rc == ENODEV)
104 {
105 // If the directory or device disappeared then this application
106 // should gracefully exit. There are race conditions between the
107 // unloading of a hwmon driver and the stopping of this service
108 // by systemd. To prevent this application from falsely failing
109 // in these scenarios, it will simply exit if the directory or
110 // file can not be found. It is up to the user(s) of this
111 // provided hwmon object to log the appropriate errors if the
112 // object disappears when it should not.
113 exit(0);
114 }
115
Patrick Venture75e56c62018-04-20 18:10:15 -0700116 if (0 == std::count(
117 retryableErrors.begin(),
118 retryableErrors.end(),
119 rc) ||
120 !retries)
121 {
122 // Not a retryable error or out of retries.
123#ifdef NEGATIVE_ERRNO_ON_FAIL
124 return -rc;
125#endif
126
127 // Work around GCC bugs 53984 and 66145 for callers by
128 // explicitly raising system_error here.
129 throw std::system_error(rc, std::generic_category());
130 }
131
132 --retries;
133 std::this_thread::sleep_for(delay);
134 continue;
135 }
136 break;
137 }
138
139 return val;
140}
141
142void HwmonIO::write(
143 uint32_t val,
144 const std::string& type,
145 const std::string& id,
146 const std::string& sensor,
147 size_t retries,
148 std::chrono::milliseconds delay) const
149
150{
151 std::ofstream ofs;
152 auto fullPath = sysfs::make_sysfs_path(
153 p, type, id, sensor);
154
155 ofs.exceptions(
156 std::ofstream::failbit |
157 std::ofstream::badbit |
158 std::ofstream::eofbit);
159
160 // See comments in the read method for an explanation of the odd exception
161 // handling behavior here.
162
163 while (true)
164 {
165 try
166 {
167 errno = 0;
168 if (!ofs.is_open())
169 ofs.open(fullPath);
170 ofs.clear();
171 ofs.seekp(0);
172 ofs << val;
173 ofs.flush();
174 }
175 catch (const std::exception& e)
176 {
177 auto rc = errno;
178
179 if (!rc)
180 {
181 throw;
182 }
183
184 if (rc == ENOENT)
185 {
186 exit(0);
187 }
188
189 if (0 == std::count(
190 retryableErrors.begin(),
191 retryableErrors.end(),
192 rc) ||
193 !retries)
194 {
195 // Not a retryable error or out of retries.
196
197 // Work around GCC bugs 53984 and 66145 for callers by
198 // explicitly raising system_error here.
199 throw std::system_error(rc, std::generic_category());
200 }
201
202 --retries;
203 std::this_thread::sleep_for(delay);
204 continue;
205 }
206 break;
207 }
208}
209
210std::string HwmonIO::path() const
211{
212 return p;
213}
214
215} // hwmonio