blob: 23f32c5257b2c0d3b5cd0bf9e1a42320c03c8ea4 [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 {
Hao Zhoubcc64532025-03-25 02:37:35 +000021 stdplus::print(stderr, "no directory at {}, creating.\n", folderPath);
kasunath37bc0df2022-06-07 12:40:26 -070022 if (!std::filesystem::create_directories(path))
23 {
Patrick Williams5de90612024-02-13 21:31:53 -060024 stdplus::print(stderr, "Failed to create a folder at {}\n",
25 folderPath);
kasunath37bc0df2022-06-07 12:40:26 -070026 return false;
27 }
28 }
29 return true;
30}
31
32bool ExternalStorerFileWriter::createFile(const std::string& folderPath,
33 const nlohmann::json& jsonPdr) const
34{
35 if (!createFolder(folderPath))
36 {
37 return false;
38 }
39 std::filesystem::path path(folderPath);
40 path /= "index.json";
41 // If the file already exist, overwrite it.
42 std::ofstream output(path);
43 output << jsonPdr;
44 output.close();
45 return true;
46}
47
Brandon Kim41a58d42024-09-06 06:49:42 +000048bool ExternalStorerFileWriter::removeAll(const std::string& filePath) const
49{
50 // Attempt to delete the file
51 std::error_code ec;
52 std::filesystem::remove_all(filePath, ec);
53 if (ec)
54 {
55 return false;
56 }
Brandon Kim41a58d42024-09-06 06:49:42 +000057 return true;
58}
59
kasunath37bc0df2022-06-07 12:40:26 -070060ExternalStorerFileInterface::ExternalStorerFileInterface(
kasunath3d0cd552022-08-25 20:22:58 -070061 const std::shared_ptr<sdbusplus::asio::connection>& conn,
62 std::string_view rootPath,
Brandon Kim41a58d42024-09-06 06:49:42 +000063 std::unique_ptr<FileHandlerInterface> fileHandler,
64 uint32_t numSavedLogEntries, uint32_t numLogEntries) :
Patrick Williams1a643562024-08-16 15:22:05 -040065 rootPath(rootPath), fileHandler(std::move(fileHandler)), logServiceId(""),
Brandon Kim41a58d42024-09-06 06:49:42 +000066 cperNotifier(std::make_unique<CperFileNotifierHandler>(conn)),
67 maxNumSavedLogEntries(numSavedLogEntries), maxNumLogEntries(numLogEntries)
kasunath37bc0df2022-06-07 12:40:26 -070068{}
69
70bool ExternalStorerFileInterface::publishJson(std::string_view jsonStr)
71{
72 nlohmann::json jsonDecoded;
73 try
74 {
75 jsonDecoded = nlohmann::json::parse(jsonStr);
76 }
77 catch (nlohmann::json::parse_error& e)
78 {
Patrick Williams5de90612024-02-13 21:31:53 -060079 stdplus::print(stderr, "JSON parse error: \n{}\n", e.what());
kasunath37bc0df2022-06-07 12:40:26 -070080 return false;
81 }
82
83 // We need to know the type to determine how to process the decoded JSON
84 // output.
85 if (!jsonDecoded.contains("@odata.type"))
86 {
Patrick Williams5de90612024-02-13 21:31:53 -060087 stdplus::print(stderr, "@odata.type field doesn't exist in:\n {}\n",
88 jsonDecoded.dump(4));
kasunath37bc0df2022-06-07 12:40:26 -070089 return false;
90 }
91
92 auto schemaType = getSchemaType(jsonDecoded);
93 if (schemaType == JsonPdrType::logEntry)
94 {
95 return processLogEntry(jsonDecoded);
96 }
97 if (schemaType == JsonPdrType::logService)
98 {
99 return processLogService(jsonDecoded);
100 }
101 return processOtherTypes(jsonDecoded);
102}
103
104JsonPdrType ExternalStorerFileInterface::getSchemaType(
105 const nlohmann::json& jsonSchema) const
106{
107 auto logEntryFound =
108 std::string(jsonSchema["@odata.type"]).find("LogEntry");
109 if (logEntryFound != std::string::npos)
110 {
111 return JsonPdrType::logEntry;
112 }
113
114 auto logServiceFound =
115 std::string(jsonSchema["@odata.type"]).find("LogService");
116 if (logServiceFound != std::string::npos)
117 {
118 return JsonPdrType::logService;
119 }
120
121 return JsonPdrType::other;
122}
123
124bool ExternalStorerFileInterface::processLogEntry(nlohmann::json& logEntry)
125{
126 // TODO: Add policies for LogEntry retention.
127 // https://github.com/openbmc/bios-bmc-smm-error-logger/issues/1.
128 if (logServiceId.empty())
129 {
Patrick Williams5de90612024-02-13 21:31:53 -0600130 stdplus::print(stderr,
131 "First need a LogService PDR with a new UUID.\n");
kasunath37bc0df2022-06-07 12:40:26 -0700132 return false;
133 }
134
Brandon Kim41a58d42024-09-06 06:49:42 +0000135 // Check to see if we are hitting the limit of filePathQueue, delete oldest
136 // log entry first before processing another entry
137 if (logEntryQueue.size() == maxNumLogEntries)
138 {
139 std::string oldestFilePath = std::move(logEntryQueue.front());
140 logEntryQueue.pop();
141
142 if (!fileHandler->removeAll(oldestFilePath))
143 {
144 stdplus::print(
145 stderr,
146 "Failed to delete the oldest entry path, not processing the next log,: {}\n",
147 oldestFilePath);
148 return false;
149 }
150 }
151
kasunath37bc0df2022-06-07 12:40:26 -0700152 std::string id = boost::uuids::to_string(randomGen());
kasunatha3b64fb2022-06-15 18:47:18 -0700153 std::string fullPath =
Patrick Williams5de90612024-02-13 21:31:53 -0600154 std::format("{}/redfish/v1/Systems/system/LogServices/{}/Entries/{}",
kasunatha3b64fb2022-06-15 18:47:18 -0700155 rootPath, logServiceId, id);
kasunath37bc0df2022-06-07 12:40:26 -0700156
157 // Populate the "Id" with the UUID we generated.
158 logEntry["Id"] = id;
159 // Remove the @odata.id from the JSON since ExternalStorer will fill it for
160 // a client.
161 logEntry.erase("@odata.id");
162
Hao Zhoubcc64532025-03-25 02:37:35 +0000163 stdplus::print(stderr, "Creating CPER file under path: {}. \n", fullPath);
kasunatha3b64fb2022-06-15 18:47:18 -0700164 if (!fileHandler->createFile(fullPath, logEntry))
165 {
Patrick Williams5de90612024-02-13 21:31:53 -0600166 stdplus::print(stderr,
167 "Failed to create a file for log entry path: {}\n",
168 fullPath);
kasunatha3b64fb2022-06-15 18:47:18 -0700169 return false;
170 }
171
172 cperNotifier->createEntry(fullPath + "/index.json");
Brandon Kim41a58d42024-09-06 06:49:42 +0000173
174 // Attempt to push to logEntrySavedQueue first, before pushing to
175 // logEntryQueue that can be popped
176 if (logEntrySavedQueue.size() < maxNumSavedLogEntries)
177 {
178 logEntrySavedQueue.push(std::move(fullPath));
179 }
180 else
181 {
182 logEntryQueue.push(std::move(fullPath));
183 }
184
kasunatha3b64fb2022-06-15 18:47:18 -0700185 return true;
kasunath37bc0df2022-06-07 12:40:26 -0700186}
187
188bool ExternalStorerFileInterface::processLogService(
189 const nlohmann::json& logService)
190{
191 if (!logService.contains("@odata.id"))
192 {
Patrick Williams5de90612024-02-13 21:31:53 -0600193 stdplus::print(stderr, "@odata.id field doesn't exist in:\n {}\n",
194 logService.dump(4));
kasunath37bc0df2022-06-07 12:40:26 -0700195 return false;
196 }
197
198 if (!logService.contains("Id"))
199 {
Patrick Williams5de90612024-02-13 21:31:53 -0600200 stdplus::print(stderr, "Id field doesn't exist in:\n {}\n",
201 logService.dump(4));
kasunath37bc0df2022-06-07 12:40:26 -0700202 return false;
203 }
204
205 logServiceId = logService["Id"].get<std::string>();
206
207 if (!createFile(logService["@odata.id"].get<std::string>(), logService))
208 {
Patrick Williams5de90612024-02-13 21:31:53 -0600209 stdplus::print(stderr,
210 "Failed to create LogService index file for:\n{}\n",
211 logService.dump(4));
kasunath37bc0df2022-06-07 12:40:26 -0700212 return false;
213 }
214 // ExternalStorer needs a .../Entries/index.json file with no data.
215 nlohmann::json jEmpty = "{}"_json;
216 return createFile(logService["@odata.id"].get<std::string>() + "/Entries",
217 jEmpty);
218}
219
220bool ExternalStorerFileInterface::processOtherTypes(
221 const nlohmann::json& jsonPdr) const
222{
223 if (!jsonPdr.contains("@odata.id"))
224 {
Patrick Williams5de90612024-02-13 21:31:53 -0600225 stdplus::print(stderr, "@odata.id field doesn't exist in:\n {}\n",
226 jsonPdr.dump(4));
kasunath37bc0df2022-06-07 12:40:26 -0700227 return false;
228 }
Hao Zhoubcc64532025-03-25 02:37:35 +0000229
230 const std::string& path = jsonPdr["@odata.id"].get<std::string>();
231
232 stdplus::print(stderr,
233 "Creating error counter file under path: {}. content: {}\n",
234 path, jsonPdr.dump());
235 return createFile(path, jsonPdr);
kasunath37bc0df2022-06-07 12:40:26 -0700236}
237
238bool ExternalStorerFileInterface::createFile(
239 const std::string& subPath, const nlohmann::json& jsonPdr) const
240{
241 return fileHandler->createFile(rootPath + subPath, jsonPdr);
242}
243
244} // namespace rde
245} // namespace bios_bmc_smm_error_logger