1 #pragma once
2 
3 #include "app.hpp"
4 #include "generated/enums/log_entry.hpp"
5 #include "query.hpp"
6 #include "registries/openbmc_message_registry.hpp"
7 #include "registries/privilege_registry.hpp"
8 #include "utils/time_utils.hpp"
9 
10 #include <cstdint>
11 #include <memory>
12 #include <string_view>
13 #include <utility>
14 #include <vector>
15 
16 namespace redfish
17 {
18 
19 inline void fillHostLoggerEntryJson(std::string_view logEntryID,
20                                     std::string_view msg,
21                                     nlohmann::json::object_t& logEntryJson)
22 {
23     // Fill in the log entry with the gathered data.
24     logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
25     logEntryJson["@odata.id"] = boost::urls::format(
26         "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries/{}",
27         BMCWEB_REDFISH_SYSTEM_URI_NAME, logEntryID);
28     logEntryJson["Name"] = "Host Logger Entry";
29     logEntryJson["Id"] = logEntryID;
30     logEntryJson["Message"] = msg;
31     logEntryJson["EntryType"] = log_entry::LogEntryType::Oem;
32     logEntryJson["Severity"] = log_entry::EventSeverity::OK;
33     logEntryJson["OemRecordFormat"] = "Host Logger Entry";
34 }
35 
36 inline void requestRoutesSystemHostLogger(App& app)
37 {
38     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/HostLogger/")
39         .privileges(redfish::privileges::getLogService)
40         .methods(boost::beast::http::verb::get)(
41             [&app](const crow::Request& req,
42                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
43                    const std::string& systemName) {
44                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
45                 {
46                     return;
47                 }
48                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
49                 {
50                     // Option currently returns no systems.  TBD
51                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
52                                                systemName);
53                     return;
54                 }
55                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
56                 {
57                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
58                                                systemName);
59                     return;
60                 }
61                 asyncResp->res.jsonValue["@odata.id"] =
62                     std::format("/redfish/v1/Systems/{}/LogServices/HostLogger",
63                                 BMCWEB_REDFISH_SYSTEM_URI_NAME);
64                 asyncResp->res.jsonValue["@odata.type"] =
65                     "#LogService.v1_2_0.LogService";
66                 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
67                 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
68                 asyncResp->res.jsonValue["Id"] = "HostLogger";
69                 asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format(
70                     "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries",
71                     BMCWEB_REDFISH_SYSTEM_URI_NAME);
72             });
73 }
74 
75 inline void requestRoutesSystemHostLoggerCollection(App& app)
76 {
77     BMCWEB_ROUTE(app,
78                  "/redfish/v1/Systems/<str>/LogServices/HostLogger/Entries/")
79         .privileges(redfish::privileges::getLogEntry)
80         .methods(
81             boost::beast::http::verb::
82                 get)([&app](const crow::Request& req,
83                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
84                             const std::string& systemName) {
85             query_param::QueryCapabilities capabilities = {
86                 .canDelegateTop = true,
87                 .canDelegateSkip = true,
88             };
89             query_param::Query delegatedQuery;
90             if (!redfish::setUpRedfishRouteWithDelegation(
91                     app, req, asyncResp, delegatedQuery, capabilities))
92             {
93                 return;
94             }
95             if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
96             {
97                 // Option currently returns no systems.  TBD
98                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
99                                            systemName);
100                 return;
101             }
102             if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
103             {
104                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
105                                            systemName);
106                 return;
107             }
108             asyncResp->res.jsonValue["@odata.id"] = std::format(
109                 "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries",
110                 BMCWEB_REDFISH_SYSTEM_URI_NAME);
111             asyncResp->res.jsonValue["@odata.type"] =
112                 "#LogEntryCollection.LogEntryCollection";
113             asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
114             asyncResp->res.jsonValue["Description"] =
115                 "Collection of HostLogger Entries";
116             nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
117             logEntryArray = nlohmann::json::array();
118             asyncResp->res.jsonValue["Members@odata.count"] = 0;
119 
120             std::vector<std::filesystem::path> hostLoggerFiles;
121             if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
122             {
123                 BMCWEB_LOG_DEBUG("Failed to get host log file path");
124                 return;
125             }
126             // If we weren't provided top and skip limits, use the defaults.
127             size_t skip = delegatedQuery.skip.value_or(0);
128             size_t top =
129                 delegatedQuery.top.value_or(query_param::Query::maxTop);
130             size_t logCount = 0;
131             // This vector only store the entries we want to expose that
132             // control by skip and top.
133             std::vector<std::string> logEntries;
134             if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries,
135                                       logCount))
136             {
137                 messages::internalError(asyncResp->res);
138                 return;
139             }
140             // If vector is empty, that means skip value larger than total
141             // log count
142             if (logEntries.empty())
143             {
144                 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
145                 return;
146             }
147             if (!logEntries.empty())
148             {
149                 for (size_t i = 0; i < logEntries.size(); i++)
150                 {
151                     nlohmann::json::object_t hostLogEntry;
152                     fillHostLoggerEntryJson(std::to_string(skip + i),
153                                             logEntries[i], hostLogEntry);
154                     logEntryArray.emplace_back(std::move(hostLogEntry));
155                 }
156 
157                 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
158                 if (skip + top < logCount)
159                 {
160                     asyncResp->res.jsonValue["Members@odata.nextLink"] =
161                         std::format(
162                             "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries?$skip=",
163                             BMCWEB_REDFISH_SYSTEM_URI_NAME) +
164                         std::to_string(skip + top);
165                 }
166             }
167         });
168 }
169 
170 inline void requestRoutesSystemHostLoggerLogEntry(App& app)
171 {
172     BMCWEB_ROUTE(
173         app, "/redfish/v1/Systems/<str>/LogServices/HostLogger/Entries/<str>/")
174         .privileges(redfish::privileges::getLogEntry)
175         .methods(boost::beast::http::verb::get)(
176             [&app](const crow::Request& req,
177                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
178                    const std::string& systemName, const std::string& param) {
179                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
180                 {
181                     return;
182                 }
183                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
184                 {
185                     // Option currently returns no systems.  TBD
186                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
187                                                systemName);
188                     return;
189                 }
190                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
191                 {
192                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
193                                                systemName);
194                     return;
195                 }
196                 std::string_view targetID = param;
197 
198                 uint64_t idInt = 0;
199 
200                 auto [ptr, ec] =
201                     std::from_chars(targetID.begin(), targetID.end(), idInt);
202                 if (ec != std::errc{} || ptr != targetID.end())
203                 {
204                     messages::resourceNotFound(asyncResp->res, "LogEntry",
205                                                param);
206                     return;
207                 }
208 
209                 std::vector<std::filesystem::path> hostLoggerFiles;
210                 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
211                 {
212                     BMCWEB_LOG_DEBUG("Failed to get host log file path");
213                     return;
214                 }
215 
216                 size_t logCount = 0;
217                 size_t top = 1;
218                 std::vector<std::string> logEntries;
219                 // We can get specific entry by skip and top. For example, if we
220                 // want to get nth entry, we can set skip = n-1 and top = 1 to
221                 // get that entry
222                 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top,
223                                           logEntries, logCount))
224                 {
225                     messages::internalError(asyncResp->res);
226                     return;
227                 }
228 
229                 if (!logEntries.empty())
230                 {
231                     nlohmann::json::object_t hostLogEntry;
232                     fillHostLoggerEntryJson(targetID, logEntries[0],
233                                             hostLogEntry);
234                     asyncResp->res.jsonValue.update(hostLogEntry);
235                     return;
236                 }
237 
238                 // Requested ID was not found
239                 messages::resourceNotFound(asyncResp->res, "LogEntry", param);
240             });
241 }
242 
243 inline void requestRoutesSystemsLogServiceHostlogger(App& app)
244 {
245     requestRoutesSystemHostLogger(app);
246     requestRoutesSystemHostLoggerCollection(app);
247     requestRoutesSystemHostLoggerLogEntry(app);
248 }
249 
250 } // namespace redfish
251