// Copyright 2021 Google Inc. // // 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 "log_handler.hpp" #include #include #include #include #include #include #include #include namespace ipmi_flash { LogBlobHandler::LogBlobHandler(std::vector>&& configs) { for (auto& config : configs) { auto info = std::make_unique(); info->blobId = std::move(config.blobId); info->actions = std::move(config.actions); info->handler = std::move(config.handler); info->actions->onOpen->setCallback( [infoP = info.get()](TriggerableActionInterface& tai) { auto data = std::make_shared>>(); do { if (tai.status() != ActionStatus::success) { fprintf(stderr, "LogBlobHandler: Log file unit failed for %s\n", infoP->blobId.c_str()); continue; } if (!infoP->handler->open("", std::ios::in)) { fprintf(stderr, "LogBlobHandler: Opening log file failed for %s\n", infoP->blobId.c_str()); continue; } auto d = infoP->handler->read( 0, std::numeric_limits::max()); infoP->handler->close(); if (!d) { fprintf(stderr, "LogBlobHandler: Reading log file failed for %s\n", infoP->blobId.c_str()); continue; } *data = std::move(d); } while (false); for (auto sessionP : infoP->sessionsToUpdate) { sessionP->data = data; } infoP->sessionsToUpdate.clear(); }); if (!blobInfoMap.try_emplace(info->blobId, std::move(info)).second) { fprintf(stderr, "LogBlobHandler: Ignoring duplicate config for %s\n", info->blobId.c_str()); } } } bool LogBlobHandler::canHandleBlob(const std::string& path) { return blobInfoMap.find(path) != blobInfoMap.end(); } std::vector LogBlobHandler::getBlobIds() { std::vector ret; for (const auto& [key, _] : blobInfoMap) { ret.emplace_back(key); } return ret; } /** * deleteBlob - does nothing, always fails */ bool LogBlobHandler::deleteBlob(const std::string& path) { for (const auto& [sessionId, sessionInfo] : sessionInfoMap) { if (sessionInfo->blob->blobId == path) { fprintf(stderr, "LogBlobHandler: delete %s fail: there is an open session " "for this blob\n", path.c_str()); return false; } } auto* blob = blobInfoMap.at(path).get(); if (!blob->actions->onDelete->trigger()) { fprintf(stderr, "LogBlobHandler: delete %s fail: onDelete trigger failed\n", path.c_str()); return false; } return true; } bool LogBlobHandler::stat(const std::string&, blobs::BlobMeta*) { return false; } bool LogBlobHandler::open(uint16_t session, uint16_t flags, const std::string& path) { /* only reads are supported, check if blob is handled and make sure * the blob isn't already opened */ if (flags != blobs::read) { fprintf(stderr, "LogBlobHandler: open %s fail: unsupported flags(0x%04X.)\n", path.c_str(), flags); return false; } auto info = std::make_unique(); info->blob = blobInfoMap.at(path).get(); info->blob->sessionsToUpdate.emplace(info.get()); if (info->blob->sessionsToUpdate.size() == 1 && !info->blob->actions->onOpen->trigger()) { fprintf(stderr, "LogBlobHandler: open %s fail: onOpen trigger failed\n", path.c_str()); info->blob->sessionsToUpdate.erase(info.get()); return false; } sessionInfoMap[session] = std::move(info); return true; } std::vector LogBlobHandler::read(uint16_t session, uint32_t offset, uint32_t requestedSize) { auto& data = sessionInfoMap.at(session)->data; if (data == nullptr || !*data) { throw std::runtime_error("LogBlobHandler: Log data not ready for read"); } if ((*data)->size() < offset) { return {}; } std::vector ret( std::min(requestedSize, (*data)->size() - offset)); std::memcpy(&ret[0], &(**data)[offset], ret.size()); return ret; } bool LogBlobHandler::close(uint16_t session) { auto it = sessionInfoMap.find(session); if (it == sessionInfoMap.end()) { return false; } auto& info = *it->second; info.blob->sessionsToUpdate.erase(&info); if (info.blob->sessionsToUpdate.empty()) { info.blob->actions->onOpen->abort(); } sessionInfoMap.erase(it); return true; } bool LogBlobHandler::stat(uint16_t session, blobs::BlobMeta* meta) { const auto& data = sessionInfoMap.at(session)->data; if (data == nullptr) { meta->blobState = blobs::StateFlags::committing; meta->size = 0; } else if (!*data) { meta->blobState = blobs::StateFlags::commit_error; meta->size = 0; } else { meta->blobState = blobs::StateFlags::committed | blobs::StateFlags::open_read; meta->size = (*data)->size(); } return true; } bool LogBlobHandler::expire(uint16_t session) { close(session); return true; } } // namespace ipmi_flash