blob: 1ffea44311bd8e0db48bf39c91f3a93197089244 [file] [log] [blame]
kasunath37bc0df2022-06-07 12:40:26 -07001#include "rde/external_storer_file.hpp"
2
kasunath37bc0df2022-06-07 12:40:26 -07003#include <boost/uuid/uuid.hpp>
4#include <boost/uuid/uuid_io.hpp>
Patrick Williams5de90612024-02-13 21:31:53 -06005#include <stdplus/print.hpp>
kasunath37bc0df2022-06-07 12:40:26 -07006
Patrick Williams5de90612024-02-13 21:31:53 -06007#include <format>
kasunath37bc0df2022-06-07 12:40:26 -07008#include <fstream>
9#include <string_view>
10
11namespace bios_bmc_smm_error_logger
12{
13namespace rde
14{
15
16bool ExternalStorerFileWriter::createFolder(const std::string& folderPath) const
17{
18 std::filesystem::path path(folderPath);
19 if (!std::filesystem::is_directory(path))
20 {
21 if (!std::filesystem::create_directories(path))
22 {
Patrick Williams5de90612024-02-13 21:31:53 -060023 stdplus::print(stderr, "Failed to create a folder at {}\n",
24 folderPath);
kasunath37bc0df2022-06-07 12:40:26 -070025 return false;
26 }
27 }
28 return true;
29}
30
31bool ExternalStorerFileWriter::createFile(const std::string& folderPath,
32 const nlohmann::json& jsonPdr) const
33{
34 if (!createFolder(folderPath))
35 {
36 return false;
37 }
38 std::filesystem::path path(folderPath);
39 path /= "index.json";
40 // If the file already exist, overwrite it.
41 std::ofstream output(path);
42 output << jsonPdr;
43 output.close();
Patrick Williams5de90612024-02-13 21:31:53 -060044 stdplus::print(stderr, "Created: {}\n", path.string());
kasunath37bc0df2022-06-07 12:40:26 -070045 return true;
46}
47
48ExternalStorerFileInterface::ExternalStorerFileInterface(
kasunath3d0cd552022-08-25 20:22:58 -070049 const std::shared_ptr<sdbusplus::asio::connection>& conn,
50 std::string_view rootPath,
kasunath37bc0df2022-06-07 12:40:26 -070051 std::unique_ptr<FileHandlerInterface> fileHandler) :
Patrick Williams1a643562024-08-16 15:22:05 -040052 rootPath(rootPath), fileHandler(std::move(fileHandler)), logServiceId(""),
kasunath3d0cd552022-08-25 20:22:58 -070053 cperNotifier(std::make_unique<CperFileNotifierHandler>(conn))
kasunath37bc0df2022-06-07 12:40:26 -070054{}
55
56bool ExternalStorerFileInterface::publishJson(std::string_view jsonStr)
57{
58 nlohmann::json jsonDecoded;
59 try
60 {
61 jsonDecoded = nlohmann::json::parse(jsonStr);
62 }
63 catch (nlohmann::json::parse_error& e)
64 {
Patrick Williams5de90612024-02-13 21:31:53 -060065 stdplus::print(stderr, "JSON parse error: \n{}\n", e.what());
kasunath37bc0df2022-06-07 12:40:26 -070066 return false;
67 }
68
69 // We need to know the type to determine how to process the decoded JSON
70 // output.
71 if (!jsonDecoded.contains("@odata.type"))
72 {
Patrick Williams5de90612024-02-13 21:31:53 -060073 stdplus::print(stderr, "@odata.type field doesn't exist in:\n {}\n",
74 jsonDecoded.dump(4));
kasunath37bc0df2022-06-07 12:40:26 -070075 return false;
76 }
77
78 auto schemaType = getSchemaType(jsonDecoded);
79 if (schemaType == JsonPdrType::logEntry)
80 {
81 return processLogEntry(jsonDecoded);
82 }
83 if (schemaType == JsonPdrType::logService)
84 {
85 return processLogService(jsonDecoded);
86 }
87 return processOtherTypes(jsonDecoded);
88}
89
90JsonPdrType ExternalStorerFileInterface::getSchemaType(
91 const nlohmann::json& jsonSchema) const
92{
93 auto logEntryFound =
94 std::string(jsonSchema["@odata.type"]).find("LogEntry");
95 if (logEntryFound != std::string::npos)
96 {
97 return JsonPdrType::logEntry;
98 }
99
100 auto logServiceFound =
101 std::string(jsonSchema["@odata.type"]).find("LogService");
102 if (logServiceFound != std::string::npos)
103 {
104 return JsonPdrType::logService;
105 }
106
107 return JsonPdrType::other;
108}
109
110bool ExternalStorerFileInterface::processLogEntry(nlohmann::json& logEntry)
111{
112 // TODO: Add policies for LogEntry retention.
113 // https://github.com/openbmc/bios-bmc-smm-error-logger/issues/1.
114 if (logServiceId.empty())
115 {
Patrick Williams5de90612024-02-13 21:31:53 -0600116 stdplus::print(stderr,
117 "First need a LogService PDR with a new UUID.\n");
kasunath37bc0df2022-06-07 12:40:26 -0700118 return false;
119 }
120
121 std::string id = boost::uuids::to_string(randomGen());
kasunatha3b64fb2022-06-15 18:47:18 -0700122 std::string fullPath =
Patrick Williams5de90612024-02-13 21:31:53 -0600123 std::format("{}/redfish/v1/Systems/system/LogServices/{}/Entries/{}",
kasunatha3b64fb2022-06-15 18:47:18 -0700124 rootPath, logServiceId, id);
kasunath37bc0df2022-06-07 12:40:26 -0700125
126 // Populate the "Id" with the UUID we generated.
127 logEntry["Id"] = id;
128 // Remove the @odata.id from the JSON since ExternalStorer will fill it for
129 // a client.
130 logEntry.erase("@odata.id");
131
kasunatha3b64fb2022-06-15 18:47:18 -0700132 if (!fileHandler->createFile(fullPath, logEntry))
133 {
Patrick Williams5de90612024-02-13 21:31:53 -0600134 stdplus::print(stderr,
135 "Failed to create a file for log entry path: {}\n",
136 fullPath);
kasunatha3b64fb2022-06-15 18:47:18 -0700137 return false;
138 }
139
140 cperNotifier->createEntry(fullPath + "/index.json");
141 return true;
kasunath37bc0df2022-06-07 12:40:26 -0700142}
143
144bool ExternalStorerFileInterface::processLogService(
145 const nlohmann::json& logService)
146{
147 if (!logService.contains("@odata.id"))
148 {
Patrick Williams5de90612024-02-13 21:31:53 -0600149 stdplus::print(stderr, "@odata.id field doesn't exist in:\n {}\n",
150 logService.dump(4));
kasunath37bc0df2022-06-07 12:40:26 -0700151 return false;
152 }
153
154 if (!logService.contains("Id"))
155 {
Patrick Williams5de90612024-02-13 21:31:53 -0600156 stdplus::print(stderr, "Id field doesn't exist in:\n {}\n",
157 logService.dump(4));
kasunath37bc0df2022-06-07 12:40:26 -0700158 return false;
159 }
160
161 logServiceId = logService["Id"].get<std::string>();
162
163 if (!createFile(logService["@odata.id"].get<std::string>(), logService))
164 {
Patrick Williams5de90612024-02-13 21:31:53 -0600165 stdplus::print(stderr,
166 "Failed to create LogService index file for:\n{}\n",
167 logService.dump(4));
kasunath37bc0df2022-06-07 12:40:26 -0700168 return false;
169 }
170 // ExternalStorer needs a .../Entries/index.json file with no data.
171 nlohmann::json jEmpty = "{}"_json;
172 return createFile(logService["@odata.id"].get<std::string>() + "/Entries",
173 jEmpty);
174}
175
176bool ExternalStorerFileInterface::processOtherTypes(
177 const nlohmann::json& jsonPdr) const
178{
179 if (!jsonPdr.contains("@odata.id"))
180 {
Patrick Williams5de90612024-02-13 21:31:53 -0600181 stdplus::print(stderr, "@odata.id field doesn't exist in:\n {}\n",
182 jsonPdr.dump(4));
kasunath37bc0df2022-06-07 12:40:26 -0700183 return false;
184 }
185 return createFile(jsonPdr["@odata.id"].get<std::string>(), jsonPdr);
186}
187
188bool ExternalStorerFileInterface::createFile(
189 const std::string& subPath, const nlohmann::json& jsonPdr) const
190{
191 return fileHandler->createFile(rootPath + subPath, jsonPdr);
192}
193
194} // namespace rde
195} // namespace bios_bmc_smm_error_logger