blob: fe167f5d0fd06ed8b248a9b431fef0b0b56b031b [file] [log] [blame]
/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <algorithm>
#include <exception>
#include <fstream>
#include <thread>
#include "config.h"
#include "hwmonio.hpp"
#include "sysfs.hpp"
namespace hwmonio {
static constexpr auto retryableErrors = {
/*
* Retry on bus or device errors or timeouts in case
* they are transient.
*/
EIO,
ETIMEDOUT,
/*
* Retry CRC errors.
*/
EBADMSG,
/*
* Some hwmon drivers do this when they aren't ready
* instead of blocking. Retry.
*/
EAGAIN,
/*
* We'll see this when for example i2c devices are
* unplugged but the driver is still bound. Retry
* rather than exit on the off chance the device is
* plugged back in and the driver doesn't do a
* remove/probe. If a remove does occur, we'll
* eventually get ENOENT.
*/
ENXIO,
/*
* Some devices return this when they are busy doing
* something else. Even if being busy isn't the cause,
* a retry still gives this app a shot at getting data
* as opposed to failing out on the first try.
*/
ENODATA,
};
HwmonIO::HwmonIO(const std::string& path) : p(path)
{
}
int64_t HwmonIO::read(
const std::string& type,
const std::string& id,
const std::string& sensor,
size_t retries,
std::chrono::milliseconds delay,
bool isOCC) const
{
int64_t val;
std::ifstream ifs;
auto fullPath = sysfs::make_sysfs_path(
p, type, id, sensor);
ifs.exceptions(
std::ifstream::failbit |
std::ifstream::badbit |
std::ifstream::eofbit);
while (true)
{
try
{
errno = 0;
if (!ifs.is_open())
ifs.open(fullPath);
ifs.clear();
ifs.seekg(0);
ifs >> val;
}
catch (const std::exception& e)
{
auto rc = errno;
if (!rc)
{
throw;
}
if (rc == ENOENT || rc == ENODEV)
{
// If the directory or device disappeared then this application
// should gracefully exit. There are race conditions between the
// unloading of a hwmon driver and the stopping of this service
// by systemd. To prevent this application from falsely failing
// in these scenarios, it will simply exit if the directory or
// file can not be found. It is up to the user(s) of this
// provided hwmon object to log the appropriate errors if the
// object disappears when it should not.
exit(0);
}
if (isOCC)
{
if (rc == EREMOTEIO)
{
// For the OCCs, when an EREMOTEIO is return, set the
// value to 255*1000
// (0xFF = sensor is failed, 1000 = sensor factor)
val = 255000;
break;
}
}
if (0 == std::count(
retryableErrors.begin(),
retryableErrors.end(),
rc) ||
!retries)
{
// Not a retryable error or out of retries.
#ifdef NEGATIVE_ERRNO_ON_FAIL
return -rc;
#endif
// Work around GCC bugs 53984 and 66145 for callers by
// explicitly raising system_error here.
throw std::system_error(rc, std::generic_category());
}
--retries;
std::this_thread::sleep_for(delay);
continue;
}
break;
}
return val;
}
void HwmonIO::write(
uint32_t val,
const std::string& type,
const std::string& id,
const std::string& sensor,
size_t retries,
std::chrono::milliseconds delay) const
{
std::ofstream ofs;
auto fullPath = sysfs::make_sysfs_path(
p, type, id, sensor);
ofs.exceptions(
std::ofstream::failbit |
std::ofstream::badbit |
std::ofstream::eofbit);
// See comments in the read method for an explanation of the odd exception
// handling behavior here.
while (true)
{
try
{
errno = 0;
if (!ofs.is_open())
ofs.open(fullPath);
ofs.clear();
ofs.seekp(0);
ofs << val;
ofs.flush();
}
catch (const std::exception& e)
{
auto rc = errno;
if (!rc)
{
throw;
}
if (rc == ENOENT)
{
exit(0);
}
if (0 == std::count(
retryableErrors.begin(),
retryableErrors.end(),
rc) ||
!retries)
{
// Not a retryable error or out of retries.
// Work around GCC bugs 53984 and 66145 for callers by
// explicitly raising system_error here.
throw std::system_error(rc, std::generic_category());
}
--retries;
std::this_thread::sleep_for(delay);
continue;
}
break;
}
}
std::string HwmonIO::path() const
{
return p;
}
} // hwmonio