137bc0df1Skasunath #include "rde/external_storer_file.hpp"
237bc0df1Skasunath
337bc0df1Skasunath #include <boost/uuid/uuid.hpp>
437bc0df1Skasunath #include <boost/uuid/uuid_io.hpp>
55de90619SPatrick Williams #include <stdplus/print.hpp>
637bc0df1Skasunath
75de90619SPatrick Williams #include <format>
837bc0df1Skasunath #include <fstream>
937bc0df1Skasunath #include <string_view>
1037bc0df1Skasunath
1137bc0df1Skasunath namespace bios_bmc_smm_error_logger
1237bc0df1Skasunath {
1337bc0df1Skasunath namespace rde
1437bc0df1Skasunath {
1537bc0df1Skasunath
createFolder(const std::string & folderPath) const1637bc0df1Skasunath bool ExternalStorerFileWriter::createFolder(const std::string& folderPath) const
1737bc0df1Skasunath {
1837bc0df1Skasunath std::filesystem::path path(folderPath);
1937bc0df1Skasunath if (!std::filesystem::is_directory(path))
2037bc0df1Skasunath {
21*bcc64531SHao Zhou stdplus::print(stderr, "no directory at {}, creating.\n", folderPath);
2237bc0df1Skasunath if (!std::filesystem::create_directories(path))
2337bc0df1Skasunath {
245de90619SPatrick Williams stdplus::print(stderr, "Failed to create a folder at {}\n",
255de90619SPatrick Williams folderPath);
2637bc0df1Skasunath return false;
2737bc0df1Skasunath }
2837bc0df1Skasunath }
2937bc0df1Skasunath return true;
3037bc0df1Skasunath }
3137bc0df1Skasunath
createFile(const std::string & folderPath,const nlohmann::json & jsonPdr) const3237bc0df1Skasunath bool ExternalStorerFileWriter::createFile(const std::string& folderPath,
3337bc0df1Skasunath const nlohmann::json& jsonPdr) const
3437bc0df1Skasunath {
3537bc0df1Skasunath if (!createFolder(folderPath))
3637bc0df1Skasunath {
3737bc0df1Skasunath return false;
3837bc0df1Skasunath }
3937bc0df1Skasunath std::filesystem::path path(folderPath);
4037bc0df1Skasunath path /= "index.json";
4137bc0df1Skasunath // If the file already exist, overwrite it.
4237bc0df1Skasunath std::ofstream output(path);
4337bc0df1Skasunath output << jsonPdr;
4437bc0df1Skasunath output.close();
4537bc0df1Skasunath return true;
4637bc0df1Skasunath }
4737bc0df1Skasunath
removeAll(const std::string & filePath) const4841a58d4fSBrandon Kim bool ExternalStorerFileWriter::removeAll(const std::string& filePath) const
4941a58d4fSBrandon Kim {
5041a58d4fSBrandon Kim // Attempt to delete the file
5141a58d4fSBrandon Kim std::error_code ec;
5241a58d4fSBrandon Kim std::filesystem::remove_all(filePath, ec);
5341a58d4fSBrandon Kim if (ec)
5441a58d4fSBrandon Kim {
5541a58d4fSBrandon Kim return false;
5641a58d4fSBrandon Kim }
5741a58d4fSBrandon Kim return true;
5841a58d4fSBrandon Kim }
5941a58d4fSBrandon Kim
ExternalStorerFileInterface(const std::shared_ptr<sdbusplus::asio::connection> & conn,std::string_view rootPath,std::unique_ptr<FileHandlerInterface> fileHandler,uint32_t numSavedLogEntries,uint32_t numLogEntries)6037bc0df1Skasunath ExternalStorerFileInterface::ExternalStorerFileInterface(
613d0cd556Skasunath const std::shared_ptr<sdbusplus::asio::connection>& conn,
623d0cd556Skasunath std::string_view rootPath,
6341a58d4fSBrandon Kim std::unique_ptr<FileHandlerInterface> fileHandler,
6441a58d4fSBrandon Kim uint32_t numSavedLogEntries, uint32_t numLogEntries) :
651a64356fSPatrick Williams rootPath(rootPath), fileHandler(std::move(fileHandler)), logServiceId(""),
6641a58d4fSBrandon Kim cperNotifier(std::make_unique<CperFileNotifierHandler>(conn)),
6741a58d4fSBrandon Kim maxNumSavedLogEntries(numSavedLogEntries), maxNumLogEntries(numLogEntries)
6837bc0df1Skasunath {}
6937bc0df1Skasunath
publishJson(std::string_view jsonStr)7037bc0df1Skasunath bool ExternalStorerFileInterface::publishJson(std::string_view jsonStr)
7137bc0df1Skasunath {
7237bc0df1Skasunath nlohmann::json jsonDecoded;
7337bc0df1Skasunath try
7437bc0df1Skasunath {
7537bc0df1Skasunath jsonDecoded = nlohmann::json::parse(jsonStr);
7637bc0df1Skasunath }
7737bc0df1Skasunath catch (nlohmann::json::parse_error& e)
7837bc0df1Skasunath {
795de90619SPatrick Williams stdplus::print(stderr, "JSON parse error: \n{}\n", e.what());
8037bc0df1Skasunath return false;
8137bc0df1Skasunath }
8237bc0df1Skasunath
8337bc0df1Skasunath // We need to know the type to determine how to process the decoded JSON
8437bc0df1Skasunath // output.
8537bc0df1Skasunath if (!jsonDecoded.contains("@odata.type"))
8637bc0df1Skasunath {
875de90619SPatrick Williams stdplus::print(stderr, "@odata.type field doesn't exist in:\n {}\n",
8837bc0df1Skasunath jsonDecoded.dump(4));
8937bc0df1Skasunath return false;
9037bc0df1Skasunath }
9137bc0df1Skasunath
9237bc0df1Skasunath auto schemaType = getSchemaType(jsonDecoded);
9337bc0df1Skasunath if (schemaType == JsonPdrType::logEntry)
9437bc0df1Skasunath {
9537bc0df1Skasunath return processLogEntry(jsonDecoded);
9637bc0df1Skasunath }
9737bc0df1Skasunath if (schemaType == JsonPdrType::logService)
9837bc0df1Skasunath {
9937bc0df1Skasunath return processLogService(jsonDecoded);
10037bc0df1Skasunath }
10137bc0df1Skasunath return processOtherTypes(jsonDecoded);
10237bc0df1Skasunath }
10337bc0df1Skasunath
getSchemaType(const nlohmann::json & jsonSchema) const10437bc0df1Skasunath JsonPdrType ExternalStorerFileInterface::getSchemaType(
10537bc0df1Skasunath const nlohmann::json& jsonSchema) const
10637bc0df1Skasunath {
10737bc0df1Skasunath auto logEntryFound =
10837bc0df1Skasunath std::string(jsonSchema["@odata.type"]).find("LogEntry");
10937bc0df1Skasunath if (logEntryFound != std::string::npos)
11037bc0df1Skasunath {
11137bc0df1Skasunath return JsonPdrType::logEntry;
11237bc0df1Skasunath }
11337bc0df1Skasunath
11437bc0df1Skasunath auto logServiceFound =
11537bc0df1Skasunath std::string(jsonSchema["@odata.type"]).find("LogService");
11637bc0df1Skasunath if (logServiceFound != std::string::npos)
11737bc0df1Skasunath {
11837bc0df1Skasunath return JsonPdrType::logService;
11937bc0df1Skasunath }
12037bc0df1Skasunath
12137bc0df1Skasunath return JsonPdrType::other;
12237bc0df1Skasunath }
12337bc0df1Skasunath
processLogEntry(nlohmann::json & logEntry)12437bc0df1Skasunath bool ExternalStorerFileInterface::processLogEntry(nlohmann::json& logEntry)
12537bc0df1Skasunath {
12637bc0df1Skasunath // TODO: Add policies for LogEntry retention.
12737bc0df1Skasunath // https://github.com/openbmc/bios-bmc-smm-error-logger/issues/1.
12837bc0df1Skasunath if (logServiceId.empty())
12937bc0df1Skasunath {
1305de90619SPatrick Williams stdplus::print(stderr,
1315de90619SPatrick Williams "First need a LogService PDR with a new UUID.\n");
13237bc0df1Skasunath return false;
13337bc0df1Skasunath }
13437bc0df1Skasunath
13541a58d4fSBrandon Kim // Check to see if we are hitting the limit of filePathQueue, delete oldest
13641a58d4fSBrandon Kim // log entry first before processing another entry
13741a58d4fSBrandon Kim if (logEntryQueue.size() == maxNumLogEntries)
13841a58d4fSBrandon Kim {
13941a58d4fSBrandon Kim std::string oldestFilePath = std::move(logEntryQueue.front());
14041a58d4fSBrandon Kim logEntryQueue.pop();
14141a58d4fSBrandon Kim
14241a58d4fSBrandon Kim if (!fileHandler->removeAll(oldestFilePath))
14341a58d4fSBrandon Kim {
14441a58d4fSBrandon Kim stdplus::print(
14541a58d4fSBrandon Kim stderr,
14641a58d4fSBrandon Kim "Failed to delete the oldest entry path, not processing the next log,: {}\n",
14741a58d4fSBrandon Kim oldestFilePath);
14841a58d4fSBrandon Kim return false;
14941a58d4fSBrandon Kim }
15041a58d4fSBrandon Kim }
15141a58d4fSBrandon Kim
15237bc0df1Skasunath std::string id = boost::uuids::to_string(randomGen());
153a3b64fb6Skasunath std::string fullPath =
1545de90619SPatrick Williams std::format("{}/redfish/v1/Systems/system/LogServices/{}/Entries/{}",
155a3b64fb6Skasunath rootPath, logServiceId, id);
15637bc0df1Skasunath
15737bc0df1Skasunath // Populate the "Id" with the UUID we generated.
15837bc0df1Skasunath logEntry["Id"] = id;
15937bc0df1Skasunath // Remove the @odata.id from the JSON since ExternalStorer will fill it for
16037bc0df1Skasunath // a client.
16137bc0df1Skasunath logEntry.erase("@odata.id");
16237bc0df1Skasunath
163*bcc64531SHao Zhou stdplus::print(stderr, "Creating CPER file under path: {}. \n", fullPath);
164a3b64fb6Skasunath if (!fileHandler->createFile(fullPath, logEntry))
165a3b64fb6Skasunath {
1665de90619SPatrick Williams stdplus::print(stderr,
1675de90619SPatrick Williams "Failed to create a file for log entry path: {}\n",
168a3b64fb6Skasunath fullPath);
169a3b64fb6Skasunath return false;
170a3b64fb6Skasunath }
171a3b64fb6Skasunath
172a3b64fb6Skasunath cperNotifier->createEntry(fullPath + "/index.json");
17341a58d4fSBrandon Kim
17441a58d4fSBrandon Kim // Attempt to push to logEntrySavedQueue first, before pushing to
17541a58d4fSBrandon Kim // logEntryQueue that can be popped
17641a58d4fSBrandon Kim if (logEntrySavedQueue.size() < maxNumSavedLogEntries)
17741a58d4fSBrandon Kim {
17841a58d4fSBrandon Kim logEntrySavedQueue.push(std::move(fullPath));
17941a58d4fSBrandon Kim }
18041a58d4fSBrandon Kim else
18141a58d4fSBrandon Kim {
18241a58d4fSBrandon Kim logEntryQueue.push(std::move(fullPath));
18341a58d4fSBrandon Kim }
18441a58d4fSBrandon Kim
185a3b64fb6Skasunath return true;
18637bc0df1Skasunath }
18737bc0df1Skasunath
processLogService(const nlohmann::json & logService)18837bc0df1Skasunath bool ExternalStorerFileInterface::processLogService(
18937bc0df1Skasunath const nlohmann::json& logService)
19037bc0df1Skasunath {
19137bc0df1Skasunath if (!logService.contains("@odata.id"))
19237bc0df1Skasunath {
1935de90619SPatrick Williams stdplus::print(stderr, "@odata.id field doesn't exist in:\n {}\n",
19437bc0df1Skasunath logService.dump(4));
19537bc0df1Skasunath return false;
19637bc0df1Skasunath }
19737bc0df1Skasunath
19837bc0df1Skasunath if (!logService.contains("Id"))
19937bc0df1Skasunath {
2005de90619SPatrick Williams stdplus::print(stderr, "Id field doesn't exist in:\n {}\n",
20137bc0df1Skasunath logService.dump(4));
20237bc0df1Skasunath return false;
20337bc0df1Skasunath }
20437bc0df1Skasunath
20537bc0df1Skasunath logServiceId = logService["Id"].get<std::string>();
20637bc0df1Skasunath
20737bc0df1Skasunath if (!createFile(logService["@odata.id"].get<std::string>(), logService))
20837bc0df1Skasunath {
2095de90619SPatrick Williams stdplus::print(stderr,
2105de90619SPatrick Williams "Failed to create LogService index file for:\n{}\n",
21137bc0df1Skasunath logService.dump(4));
21237bc0df1Skasunath return false;
21337bc0df1Skasunath }
21437bc0df1Skasunath // ExternalStorer needs a .../Entries/index.json file with no data.
21537bc0df1Skasunath nlohmann::json jEmpty = "{}"_json;
21637bc0df1Skasunath return createFile(logService["@odata.id"].get<std::string>() + "/Entries",
21737bc0df1Skasunath jEmpty);
21837bc0df1Skasunath }
21937bc0df1Skasunath
processOtherTypes(const nlohmann::json & jsonPdr) const22037bc0df1Skasunath bool ExternalStorerFileInterface::processOtherTypes(
22137bc0df1Skasunath const nlohmann::json& jsonPdr) const
22237bc0df1Skasunath {
22337bc0df1Skasunath if (!jsonPdr.contains("@odata.id"))
22437bc0df1Skasunath {
2255de90619SPatrick Williams stdplus::print(stderr, "@odata.id field doesn't exist in:\n {}\n",
22637bc0df1Skasunath jsonPdr.dump(4));
22737bc0df1Skasunath return false;
22837bc0df1Skasunath }
229*bcc64531SHao Zhou
230*bcc64531SHao Zhou const std::string& path = jsonPdr["@odata.id"].get<std::string>();
231*bcc64531SHao Zhou
232*bcc64531SHao Zhou stdplus::print(stderr,
233*bcc64531SHao Zhou "Creating error counter file under path: {}. content: {}\n",
234*bcc64531SHao Zhou path, jsonPdr.dump());
235*bcc64531SHao Zhou return createFile(path, jsonPdr);
23637bc0df1Skasunath }
23737bc0df1Skasunath
createFile(const std::string & subPath,const nlohmann::json & jsonPdr) const23837bc0df1Skasunath bool ExternalStorerFileInterface::createFile(
23937bc0df1Skasunath const std::string& subPath, const nlohmann::json& jsonPdr) const
24037bc0df1Skasunath {
24137bc0df1Skasunath return fileHandler->createFile(rootPath + subPath, jsonPdr);
24237bc0df1Skasunath }
24337bc0df1Skasunath
24437bc0df1Skasunath } // namespace rde
24537bc0df1Skasunath } // namespace bios_bmc_smm_error_logger
246