blob: d3f577f889f1bef5c55ebf8ef216ab24b77fa678 [file] [log] [blame]
#include "NVMeMetricStore.hpp"
#include <xyz/openbmc_project/Common/File/error.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
sdbusplus::message::unix_fd
MetricStore::writeToFd(std::span<const uint8_t> header,
std::span<const uint8_t> data)
{
std::array<int, 2> pipe{};
if (::pipe(pipe.data()) < 0)
{
std::cerr << "Metric Fetcher failed due to pipe creation "
<< std::strerror(errno) << "\n";
throw sdbusplus::xyz::openbmc_project::Common::File::Error::Open{};
}
auto asyncPipe =
std::make_shared<boost::asio::posix::stream_descriptor>(io, pipe[1]);
std::weak_ptr<boost::asio::posix::stream_descriptor> weakPipe = asyncPipe;
auto asyncBuffer = std::make_shared<std::vector<uint8_t>>(header.begin(),
header.end());
asyncBuffer->insert(asyncBuffer->end(), data.begin(), data.end());
boost::asio::async_write(
*asyncPipe, boost::asio::buffer(*asyncBuffer),
[asyncPipe, asyncBuffer](const boost::system::error_code& error,
std::size_t /* transferredSize */) {
if (error)
{
std::cerr << "Metric Fetcher fails to write fd: " << error.message()
<< "\n";
}
});
// close the read end of the pipe after waiting for 5 seconds
// It is expected that the client has duplicated the fd in this time
auto timer = std::make_shared<boost::asio::steady_timer>(io);
timer->expires_after(std::chrono::seconds(5));
timer->async_wait([timer, fd{pipe[0]},
weakPipe](const boost::system::error_code& /* ec */) {
auto pipe = weakPipe.lock();
if (pipe)
{
pipe->cancel();
}
close(fd);
});
// Return the read end wrapped in a stream descriptor
return sdbusplus::message::unix_fd{pipe[0]};
}
sdbusplus::message::unix_fd MetricStore::getMetric(std::string name)
{
auto it = metricStore.find(name);
if (it == metricStore.end() || !it->second)
{
std::cerr << std::format("[{}, {}]cannot find the target Metric",
this->path, name);
throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument{};
}
if (!it->second->isCacheValid())
{
std::cerr << std::format("[{}, {}]the Metric cache is invalid",
this->path, name);
throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable{};
}
auto data = it->second->getCache();
// Send the data and header to be written to pipe
MetricHeader header{};
header.lens = boost::endian::little_uint32_t(sizeof(header));
header.version = 0;
header.dataFormat = 0;
header.startTime = boost::endian::little_uint64_t(
std::get<0>(data).time_since_epoch().count());
header.finishTime = boost::endian::little_uint64_t(
std::get<1>(data).time_since_epoch().count());
return writeToFd(
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
std::span<uint8_t>(reinterpret_cast<std::uint8_t*>(&header),
sizeof(header)),
std::get<2>(data));
}
void MetricStore::signalHelper(
std::string_view name,
const std::shared_ptr<MetricBase<std::chrono::steady_clock>>&
metric) noexcept
{
if (!metric->isCacheValid())
{
// broadcast invalid metric signal
metricUpdated(std::string{name}, {});
return;
}
auto data = metric->getCache();
std::vector<uint8_t> headerBuf;
headerBuf.resize(sizeof(MetricHeader));
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
MetricHeader& header = *reinterpret_cast<MetricHeader*>(headerBuf.data());
header.lens = boost::endian::little_uint32_t(sizeof(header));
header.version = 0;
header.dataFormat = 0;
header.startTime = boost::endian::little_uint64_t(
std::get<0>(data).time_since_epoch().count());
header.finishTime = boost::endian::little_uint64_t(
std::get<1>(data).time_since_epoch().count());
metricUpdated(std::string{name}, headerBuf);
};