| #include "rde/external_storer_file.hpp" |
| |
| #include <boost/uuid/uuid.hpp> |
| #include <boost/uuid/uuid_io.hpp> |
| #include <stdplus/print.hpp> |
| |
| #include <format> |
| #include <fstream> |
| #include <string_view> |
| |
| namespace bios_bmc_smm_error_logger |
| { |
| namespace rde |
| { |
| |
| bool ExternalStorerFileWriter::createFolder(const std::string& folderPath) const |
| { |
| std::filesystem::path path(folderPath); |
| if (!std::filesystem::is_directory(path)) |
| { |
| if (!std::filesystem::create_directories(path)) |
| { |
| stdplus::print(stderr, "Failed to create a folder at {}\n", |
| folderPath); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool ExternalStorerFileWriter::createFile(const std::string& folderPath, |
| const nlohmann::json& jsonPdr) const |
| { |
| if (!createFolder(folderPath)) |
| { |
| return false; |
| } |
| std::filesystem::path path(folderPath); |
| path /= "index.json"; |
| // If the file already exist, overwrite it. |
| std::ofstream output(path); |
| output << jsonPdr; |
| output.close(); |
| stdplus::print(stderr, "Created: {}\n", path.string()); |
| return true; |
| } |
| |
| bool ExternalStorerFileWriter::removeAll(const std::string& filePath) const |
| { |
| // Attempt to delete the file |
| std::error_code ec; |
| std::filesystem::remove_all(filePath, ec); |
| if (ec) |
| { |
| return false; |
| } |
| stdplus::print(stderr, "Removed: {}\n", filePath); |
| return true; |
| } |
| |
| ExternalStorerFileInterface::ExternalStorerFileInterface( |
| const std::shared_ptr<sdbusplus::asio::connection>& conn, |
| std::string_view rootPath, |
| std::unique_ptr<FileHandlerInterface> fileHandler, |
| uint32_t numSavedLogEntries, uint32_t numLogEntries) : |
| rootPath(rootPath), fileHandler(std::move(fileHandler)), logServiceId(""), |
| cperNotifier(std::make_unique<CperFileNotifierHandler>(conn)), |
| maxNumSavedLogEntries(numSavedLogEntries), maxNumLogEntries(numLogEntries) |
| {} |
| |
| bool ExternalStorerFileInterface::publishJson(std::string_view jsonStr) |
| { |
| nlohmann::json jsonDecoded; |
| try |
| { |
| jsonDecoded = nlohmann::json::parse(jsonStr); |
| } |
| catch (nlohmann::json::parse_error& e) |
| { |
| stdplus::print(stderr, "JSON parse error: \n{}\n", e.what()); |
| return false; |
| } |
| |
| // We need to know the type to determine how to process the decoded JSON |
| // output. |
| if (!jsonDecoded.contains("@odata.type")) |
| { |
| stdplus::print(stderr, "@odata.type field doesn't exist in:\n {}\n", |
| jsonDecoded.dump(4)); |
| return false; |
| } |
| |
| auto schemaType = getSchemaType(jsonDecoded); |
| if (schemaType == JsonPdrType::logEntry) |
| { |
| return processLogEntry(jsonDecoded); |
| } |
| if (schemaType == JsonPdrType::logService) |
| { |
| return processLogService(jsonDecoded); |
| } |
| return processOtherTypes(jsonDecoded); |
| } |
| |
| JsonPdrType ExternalStorerFileInterface::getSchemaType( |
| const nlohmann::json& jsonSchema) const |
| { |
| auto logEntryFound = |
| std::string(jsonSchema["@odata.type"]).find("LogEntry"); |
| if (logEntryFound != std::string::npos) |
| { |
| return JsonPdrType::logEntry; |
| } |
| |
| auto logServiceFound = |
| std::string(jsonSchema["@odata.type"]).find("LogService"); |
| if (logServiceFound != std::string::npos) |
| { |
| return JsonPdrType::logService; |
| } |
| |
| return JsonPdrType::other; |
| } |
| |
| bool ExternalStorerFileInterface::processLogEntry(nlohmann::json& logEntry) |
| { |
| // TODO: Add policies for LogEntry retention. |
| // https://github.com/openbmc/bios-bmc-smm-error-logger/issues/1. |
| if (logServiceId.empty()) |
| { |
| stdplus::print(stderr, |
| "First need a LogService PDR with a new UUID.\n"); |
| return false; |
| } |
| |
| // Check to see if we are hitting the limit of filePathQueue, delete oldest |
| // log entry first before processing another entry |
| if (logEntryQueue.size() == maxNumLogEntries) |
| { |
| std::string oldestFilePath = std::move(logEntryQueue.front()); |
| logEntryQueue.pop(); |
| |
| if (!fileHandler->removeAll(oldestFilePath)) |
| { |
| stdplus::print( |
| stderr, |
| "Failed to delete the oldest entry path, not processing the next log,: {}\n", |
| oldestFilePath); |
| return false; |
| } |
| } |
| |
| std::string id = boost::uuids::to_string(randomGen()); |
| std::string fullPath = |
| std::format("{}/redfish/v1/Systems/system/LogServices/{}/Entries/{}", |
| rootPath, logServiceId, id); |
| |
| // Populate the "Id" with the UUID we generated. |
| logEntry["Id"] = id; |
| // Remove the @odata.id from the JSON since ExternalStorer will fill it for |
| // a client. |
| logEntry.erase("@odata.id"); |
| |
| if (!fileHandler->createFile(fullPath, logEntry)) |
| { |
| stdplus::print(stderr, |
| "Failed to create a file for log entry path: {}\n", |
| fullPath); |
| return false; |
| } |
| |
| cperNotifier->createEntry(fullPath + "/index.json"); |
| |
| // Attempt to push to logEntrySavedQueue first, before pushing to |
| // logEntryQueue that can be popped |
| if (logEntrySavedQueue.size() < maxNumSavedLogEntries) |
| { |
| logEntrySavedQueue.push(std::move(fullPath)); |
| } |
| else |
| { |
| logEntryQueue.push(std::move(fullPath)); |
| } |
| |
| return true; |
| } |
| |
| bool ExternalStorerFileInterface::processLogService( |
| const nlohmann::json& logService) |
| { |
| if (!logService.contains("@odata.id")) |
| { |
| stdplus::print(stderr, "@odata.id field doesn't exist in:\n {}\n", |
| logService.dump(4)); |
| return false; |
| } |
| |
| if (!logService.contains("Id")) |
| { |
| stdplus::print(stderr, "Id field doesn't exist in:\n {}\n", |
| logService.dump(4)); |
| return false; |
| } |
| |
| logServiceId = logService["Id"].get<std::string>(); |
| |
| if (!createFile(logService["@odata.id"].get<std::string>(), logService)) |
| { |
| stdplus::print(stderr, |
| "Failed to create LogService index file for:\n{}\n", |
| logService.dump(4)); |
| return false; |
| } |
| // ExternalStorer needs a .../Entries/index.json file with no data. |
| nlohmann::json jEmpty = "{}"_json; |
| return createFile(logService["@odata.id"].get<std::string>() + "/Entries", |
| jEmpty); |
| } |
| |
| bool ExternalStorerFileInterface::processOtherTypes( |
| const nlohmann::json& jsonPdr) const |
| { |
| if (!jsonPdr.contains("@odata.id")) |
| { |
| stdplus::print(stderr, "@odata.id field doesn't exist in:\n {}\n", |
| jsonPdr.dump(4)); |
| return false; |
| } |
| return createFile(jsonPdr["@odata.id"].get<std::string>(), jsonPdr); |
| } |
| |
| bool ExternalStorerFileInterface::createFile( |
| const std::string& subPath, const nlohmann::json& jsonPdr) const |
| { |
| return fileHandler->createFile(rootPath + subPath, jsonPdr); |
| } |
| |
| } // namespace rde |
| } // namespace bios_bmc_smm_error_logger |