| #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); |
| }; |