xref: /openbmc/bmcweb/features/redfish/lib/manager_logservices_journal.hpp (revision 7da633f00b5d1499e53f43a5f0c7ea106f4dd5ee)
1b0983db2SEd Tanous #pragma once
2b0983db2SEd Tanous 
3b0983db2SEd Tanous #include "app.hpp"
4b0983db2SEd Tanous #include "error_messages.hpp"
5b0983db2SEd Tanous #include "generated/enums/log_entry.hpp"
6b0983db2SEd Tanous #include "query.hpp"
7b0983db2SEd Tanous #include "registries/base_message_registry.hpp"
8b0983db2SEd Tanous #include "registries/privilege_registry.hpp"
9b0983db2SEd Tanous #include "utils/time_utils.hpp"
10b0983db2SEd Tanous 
11b0983db2SEd Tanous #include <systemd/sd-journal.h>
12b0983db2SEd Tanous 
13b0983db2SEd Tanous #include <boost/beast/http/verb.hpp>
14b0983db2SEd Tanous 
15b0983db2SEd Tanous #include <array>
16b0983db2SEd Tanous #include <memory>
17b0983db2SEd Tanous #include <string>
18b0983db2SEd Tanous #include <string_view>
19b0983db2SEd Tanous 
20b0983db2SEd Tanous namespace redfish
21b0983db2SEd Tanous {
22b0983db2SEd Tanous 
23*7da633f0SEd Tanous inline int getJournalMetadata(sd_journal* journal, const char* field,
24b0983db2SEd Tanous                               std::string_view& contents)
25b0983db2SEd Tanous {
26b0983db2SEd Tanous     const char* data = nullptr;
27b0983db2SEd Tanous     size_t length = 0;
28b0983db2SEd Tanous     int ret = 0;
29b0983db2SEd Tanous     // Get the metadata from the requested field of the journal entry
30b0983db2SEd Tanous     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
31b0983db2SEd Tanous     const void** dataVoid = reinterpret_cast<const void**>(&data);
32b0983db2SEd Tanous 
33*7da633f0SEd Tanous     ret = sd_journal_get_data(journal, field, dataVoid, &length);
34b0983db2SEd Tanous     if (ret < 0)
35b0983db2SEd Tanous     {
36b0983db2SEd Tanous         return ret;
37b0983db2SEd Tanous     }
38b0983db2SEd Tanous     contents = std::string_view(data, length);
39b0983db2SEd Tanous     // Only use the content after the "=" character.
40b0983db2SEd Tanous     contents.remove_prefix(std::min(contents.find('=') + 1, contents.size()));
41b0983db2SEd Tanous     return ret;
42b0983db2SEd Tanous }
43b0983db2SEd Tanous 
44*7da633f0SEd Tanous inline int getJournalMetadataInt(sd_journal* journal, const char* field,
45*7da633f0SEd Tanous                                  const int base, long int& contents)
46b0983db2SEd Tanous {
47b0983db2SEd Tanous     std::string_view metadata;
48b0983db2SEd Tanous     // Get the metadata from the requested field of the journal entry
49*7da633f0SEd Tanous     int ret = getJournalMetadata(journal, field, metadata);
50b0983db2SEd Tanous     if (ret < 0)
51b0983db2SEd Tanous     {
52b0983db2SEd Tanous         return ret;
53b0983db2SEd Tanous     }
54*7da633f0SEd Tanous     std::from_chars_result res =
55*7da633f0SEd Tanous         std::from_chars(&*metadata.begin(), &*metadata.end(), contents, base);
56*7da633f0SEd Tanous     if (res.ec != std::error_code{} || res.ptr != &*metadata.end())
57*7da633f0SEd Tanous     {
58*7da633f0SEd Tanous         return -1;
59*7da633f0SEd Tanous     }
60*7da633f0SEd Tanous     return 0;
61b0983db2SEd Tanous }
62b0983db2SEd Tanous 
63b0983db2SEd Tanous inline bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp)
64b0983db2SEd Tanous {
65b0983db2SEd Tanous     int ret = 0;
66b0983db2SEd Tanous     uint64_t timestamp = 0;
67b0983db2SEd Tanous     ret = sd_journal_get_realtime_usec(journal, &timestamp);
68b0983db2SEd Tanous     if (ret < 0)
69b0983db2SEd Tanous     {
70*7da633f0SEd Tanous         BMCWEB_LOG_ERROR("Failed to read entry timestamp: {}", ret);
71b0983db2SEd Tanous         return false;
72b0983db2SEd Tanous     }
73b0983db2SEd Tanous     entryTimestamp = redfish::time_utils::getDateTimeUintUs(timestamp);
74b0983db2SEd Tanous     return true;
75b0983db2SEd Tanous }
76b0983db2SEd Tanous 
77bd79bce8SPatrick Williams inline bool fillBMCJournalLogEntryJson(
788274eb11SEd Tanous     sd_journal* journal, nlohmann::json::object_t& bmcJournalLogEntryJson)
79b0983db2SEd Tanous {
808274eb11SEd Tanous     char* cursor = nullptr;
818274eb11SEd Tanous     int ret = sd_journal_get_cursor(journal, &cursor);
828274eb11SEd Tanous     if (ret < 0)
838274eb11SEd Tanous     {
848274eb11SEd Tanous         return false;
858274eb11SEd Tanous     }
868274eb11SEd Tanous     std::unique_ptr<char*> cursorptr = std::make_unique<char*>(cursor);
878274eb11SEd Tanous     std::string bmcJournalLogEntryID(cursor);
888274eb11SEd Tanous 
89b0983db2SEd Tanous     // Get the Log Entry contents
90b0983db2SEd Tanous     std::string message;
91b0983db2SEd Tanous     std::string_view syslogID;
928274eb11SEd Tanous     ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
93b0983db2SEd Tanous     if (ret < 0)
94b0983db2SEd Tanous     {
95*7da633f0SEd Tanous         BMCWEB_LOG_DEBUG("Failed to read SYSLOG_IDENTIFIER field: {}", ret);
96b0983db2SEd Tanous     }
97b0983db2SEd Tanous     if (!syslogID.empty())
98b0983db2SEd Tanous     {
99b0983db2SEd Tanous         message += std::string(syslogID) + ": ";
100b0983db2SEd Tanous     }
101b0983db2SEd Tanous 
102b0983db2SEd Tanous     std::string_view msg;
103b0983db2SEd Tanous     ret = getJournalMetadata(journal, "MESSAGE", msg);
104b0983db2SEd Tanous     if (ret < 0)
105b0983db2SEd Tanous     {
106*7da633f0SEd Tanous         BMCWEB_LOG_ERROR("Failed to read MESSAGE field: {}", ret);
107055713e4SEd Tanous         return false;
108b0983db2SEd Tanous     }
109b0983db2SEd Tanous     message += std::string(msg);
110b0983db2SEd Tanous 
111b0983db2SEd Tanous     // Get the severity from the PRIORITY field
112b0983db2SEd Tanous     long int severity = 8; // Default to an invalid priority
113b0983db2SEd Tanous     ret = getJournalMetadataInt(journal, "PRIORITY", 10, severity);
114b0983db2SEd Tanous     if (ret < 0)
115b0983db2SEd Tanous     {
116*7da633f0SEd Tanous         BMCWEB_LOG_DEBUG("Failed to read PRIORITY field: {}", ret);
117b0983db2SEd Tanous     }
118b0983db2SEd Tanous 
119b0983db2SEd Tanous     // Get the Created time from the timestamp
120b0983db2SEd Tanous     std::string entryTimeStr;
121b0983db2SEd Tanous     if (!getEntryTimestamp(journal, entryTimeStr))
122b0983db2SEd Tanous     {
123055713e4SEd Tanous         return false;
124b0983db2SEd Tanous     }
125b0983db2SEd Tanous 
126b0983db2SEd Tanous     // Fill in the log entry with the gathered data
127b0983db2SEd Tanous     bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
1288274eb11SEd Tanous 
1298274eb11SEd Tanous     std::string entryIdBase64 =
1308274eb11SEd Tanous         crow::utility::base64encode(bmcJournalLogEntryID);
1318274eb11SEd Tanous 
132b0983db2SEd Tanous     bmcJournalLogEntryJson["@odata.id"] = boost::urls::format(
133b0983db2SEd Tanous         "/redfish/v1/Managers/{}/LogServices/Journal/Entries/{}",
1348274eb11SEd Tanous         BMCWEB_REDFISH_MANAGER_URI_NAME, entryIdBase64);
135b0983db2SEd Tanous     bmcJournalLogEntryJson["Name"] = "BMC Journal Entry";
1368274eb11SEd Tanous     bmcJournalLogEntryJson["Id"] = entryIdBase64;
137b0983db2SEd Tanous     bmcJournalLogEntryJson["Message"] = std::move(message);
138539d8c6bSEd Tanous     bmcJournalLogEntryJson["EntryType"] = log_entry::LogEntryType::Oem;
139b0983db2SEd Tanous     log_entry::EventSeverity severityEnum = log_entry::EventSeverity::OK;
140b0983db2SEd Tanous     if (severity <= 2)
141b0983db2SEd Tanous     {
142b0983db2SEd Tanous         severityEnum = log_entry::EventSeverity::Critical;
143b0983db2SEd Tanous     }
144b0983db2SEd Tanous     else if (severity <= 4)
145b0983db2SEd Tanous     {
146b0983db2SEd Tanous         severityEnum = log_entry::EventSeverity::Warning;
147b0983db2SEd Tanous     }
148b0983db2SEd Tanous 
149b0983db2SEd Tanous     bmcJournalLogEntryJson["Severity"] = severityEnum;
150b0983db2SEd Tanous     bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry";
151b0983db2SEd Tanous     bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr);
152055713e4SEd Tanous     return true;
153b0983db2SEd Tanous }
154b0983db2SEd Tanous 
15584177a2fSEd Tanous inline void handleManagersLogServiceJournalGet(
15684177a2fSEd Tanous     App& app, const crow::Request& req,
157b0983db2SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
15884177a2fSEd Tanous     const std::string& managerId)
15984177a2fSEd Tanous {
160b0983db2SEd Tanous     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
161b0983db2SEd Tanous     {
162b0983db2SEd Tanous         return;
163b0983db2SEd Tanous     }
164b0983db2SEd Tanous 
165b0983db2SEd Tanous     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
166b0983db2SEd Tanous     {
167b0983db2SEd Tanous         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
168b0983db2SEd Tanous         return;
169b0983db2SEd Tanous     }
170b0983db2SEd Tanous 
17184177a2fSEd Tanous     asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
172b0983db2SEd Tanous     asyncResp->res.jsonValue["@odata.id"] =
173b0983db2SEd Tanous         boost::urls::format("/redfish/v1/Managers/{}/LogServices/Journal",
174b0983db2SEd Tanous                             BMCWEB_REDFISH_MANAGER_URI_NAME);
175b0983db2SEd Tanous     asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
176b0983db2SEd Tanous     asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
177b0983db2SEd Tanous     asyncResp->res.jsonValue["Id"] = "Journal";
178b0983db2SEd Tanous     asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
179b0983db2SEd Tanous 
180b0983db2SEd Tanous     std::pair<std::string, std::string> redfishDateTimeOffset =
181b0983db2SEd Tanous         redfish::time_utils::getDateTimeOffsetNow();
182b0983db2SEd Tanous     asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
183b0983db2SEd Tanous     asyncResp->res.jsonValue["DateTimeLocalOffset"] =
184b0983db2SEd Tanous         redfishDateTimeOffset.second;
185b0983db2SEd Tanous 
186b0983db2SEd Tanous     asyncResp->res.jsonValue["Entries"]["@odata.id"] = boost::urls::format(
187b0983db2SEd Tanous         "/redfish/v1/Managers/{}/LogServices/Journal/Entries",
188b0983db2SEd Tanous         BMCWEB_REDFISH_MANAGER_URI_NAME);
189b0983db2SEd Tanous }
190b0983db2SEd Tanous 
191055713e4SEd Tanous struct JournalReadState
192055713e4SEd Tanous {
193055713e4SEd Tanous     std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal;
194055713e4SEd Tanous };
195055713e4SEd Tanous 
196bd79bce8SPatrick Williams inline void readJournalEntries(
197bd79bce8SPatrick Williams     uint64_t topEntryCount, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
198055713e4SEd Tanous     JournalReadState&& readState)
199055713e4SEd Tanous {
200055713e4SEd Tanous     nlohmann::json& logEntry = asyncResp->res.jsonValue["Members"];
201055713e4SEd Tanous     nlohmann::json::array_t* logEntryArray =
202055713e4SEd Tanous         logEntry.get_ptr<nlohmann::json::array_t*>();
203055713e4SEd Tanous     if (logEntryArray == nullptr)
204055713e4SEd Tanous     {
205055713e4SEd Tanous         messages::internalError(asyncResp->res);
206055713e4SEd Tanous         return;
207055713e4SEd Tanous     }
208055713e4SEd Tanous 
209055713e4SEd Tanous     // The Journal APIs unfortunately do blocking calls to the filesystem, and
210055713e4SEd Tanous     // can be somewhat expensive.  Short of creating our own io_uring based
211055713e4SEd Tanous     // implementation of sd-journal, which would be difficult, the best thing we
212055713e4SEd Tanous     // can do is to only parse a certain number of entries at a time.  The
213055713e4SEd Tanous     // current chunk size is selected arbitrarily to ensure that we're not
214055713e4SEd Tanous     // trying to process thousands of entries at the same time.
215055713e4SEd Tanous     // The implementation will process the number of entries, then return
216055713e4SEd Tanous     // control to the io_context to let other operations continue.
217055713e4SEd Tanous     size_t segmentCountRemaining = 10;
218055713e4SEd Tanous 
219055713e4SEd Tanous     // Reset the unique ID on the first entry
220055713e4SEd Tanous     for (uint64_t entryCount = logEntryArray->size();
221055713e4SEd Tanous          entryCount < topEntryCount; entryCount++)
222055713e4SEd Tanous     {
223055713e4SEd Tanous         if (segmentCountRemaining == 0)
224055713e4SEd Tanous         {
225055713e4SEd Tanous             boost::asio::post(crow::connections::systemBus->get_io_context(),
226055713e4SEd Tanous                               [asyncResp, topEntryCount,
227055713e4SEd Tanous                                readState = std::move(readState)]() mutable {
228055713e4SEd Tanous                                   readJournalEntries(topEntryCount, asyncResp,
229055713e4SEd Tanous                                                      std::move(readState));
230055713e4SEd Tanous                               });
231055713e4SEd Tanous             return;
232055713e4SEd Tanous         }
233055713e4SEd Tanous 
234055713e4SEd Tanous         nlohmann::json::object_t bmcJournalLogEntry;
2358274eb11SEd Tanous         if (!fillBMCJournalLogEntryJson(readState.journal.get(),
236055713e4SEd Tanous                                         bmcJournalLogEntry))
237055713e4SEd Tanous         {
238055713e4SEd Tanous             messages::internalError(asyncResp->res);
239055713e4SEd Tanous             return;
240055713e4SEd Tanous         }
241055713e4SEd Tanous         logEntryArray->emplace_back(std::move(bmcJournalLogEntry));
242055713e4SEd Tanous 
2438274eb11SEd Tanous         int ret = sd_journal_next(readState.journal.get());
244055713e4SEd Tanous         if (ret < 0)
245055713e4SEd Tanous         {
246055713e4SEd Tanous             messages::internalError(asyncResp->res);
247055713e4SEd Tanous             return;
248055713e4SEd Tanous         }
249055713e4SEd Tanous         if (ret == 0)
250055713e4SEd Tanous         {
251055713e4SEd Tanous             break;
252055713e4SEd Tanous         }
253055713e4SEd Tanous         segmentCountRemaining--;
254055713e4SEd Tanous     }
255055713e4SEd Tanous }
256055713e4SEd Tanous 
25784177a2fSEd Tanous inline void handleManagersJournalLogEntryCollectionGet(
25884177a2fSEd Tanous     App& app, const crow::Request& req,
259b0983db2SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
26084177a2fSEd Tanous     const std::string& managerId)
26184177a2fSEd Tanous {
262b0983db2SEd Tanous     query_param::QueryCapabilities capabilities = {
263b0983db2SEd Tanous         .canDelegateTop = true,
264b0983db2SEd Tanous         .canDelegateSkip = true,
265b0983db2SEd Tanous     };
266b0983db2SEd Tanous     query_param::Query delegatedQuery;
26784177a2fSEd Tanous     if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp,
26884177a2fSEd Tanous                                                   delegatedQuery, capabilities))
269b0983db2SEd Tanous     {
270b0983db2SEd Tanous         return;
271b0983db2SEd Tanous     }
272b0983db2SEd Tanous 
273b0983db2SEd Tanous     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
274b0983db2SEd Tanous     {
275b0983db2SEd Tanous         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
276b0983db2SEd Tanous         return;
277b0983db2SEd Tanous     }
278b0983db2SEd Tanous 
279b0983db2SEd Tanous     size_t skip = delegatedQuery.skip.value_or(0);
280b0983db2SEd Tanous     size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
281b0983db2SEd Tanous 
282b0983db2SEd Tanous     // Collections don't include the static data added by SubRoute
283b0983db2SEd Tanous     // because it has a duplicate entry for members
284b0983db2SEd Tanous     asyncResp->res.jsonValue["@odata.type"] =
285b0983db2SEd Tanous         "#LogEntryCollection.LogEntryCollection";
286b0983db2SEd Tanous     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
287b0983db2SEd Tanous         "/redfish/v1/Managers/{}/LogServices/Journal/Entries",
288b0983db2SEd Tanous         BMCWEB_REDFISH_MANAGER_URI_NAME);
289b0983db2SEd Tanous     asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
290b0983db2SEd Tanous     asyncResp->res.jsonValue["Description"] =
291b0983db2SEd Tanous         "Collection of BMC Journal Entries";
292055713e4SEd Tanous     asyncResp->res.jsonValue["Members"] = nlohmann::json::array_t();
293b0983db2SEd Tanous 
294b0983db2SEd Tanous     // Go through the journal and use the timestamp to create a
295b0983db2SEd Tanous     // unique ID for each entry
296b0983db2SEd Tanous     sd_journal* journalTmp = nullptr;
297b0983db2SEd Tanous     int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
298b0983db2SEd Tanous     if (ret < 0)
299b0983db2SEd Tanous     {
300*7da633f0SEd Tanous         BMCWEB_LOG_ERROR("failed to open journal: {}", ret);
301b0983db2SEd Tanous         messages::internalError(asyncResp->res);
302b0983db2SEd Tanous         return;
303b0983db2SEd Tanous     }
304055713e4SEd Tanous 
305b0983db2SEd Tanous     std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
306b0983db2SEd Tanous         journalTmp, sd_journal_close);
307b0983db2SEd Tanous     journalTmp = nullptr;
308b0983db2SEd Tanous 
309055713e4SEd Tanous     // Seek to the end
310055713e4SEd Tanous     if (sd_journal_seek_tail(journal.get()) < 0)
311b0983db2SEd Tanous     {
312b0983db2SEd Tanous         messages::internalError(asyncResp->res);
313b0983db2SEd Tanous         return;
314b0983db2SEd Tanous     }
315055713e4SEd Tanous 
316055713e4SEd Tanous     // Get the last entry
317055713e4SEd Tanous     if (sd_journal_previous(journal.get()) < 0)
318055713e4SEd Tanous     {
319055713e4SEd Tanous         messages::internalError(asyncResp->res);
320055713e4SEd Tanous         return;
321b0983db2SEd Tanous     }
322055713e4SEd Tanous 
323055713e4SEd Tanous     // Get the last sequence number
324055713e4SEd Tanous     uint64_t endSeqNum = 0;
325058f54edSPatrick Williams #if LIBSYSTEMD_VERSION >= 254
326055713e4SEd Tanous     {
327055713e4SEd Tanous         if (sd_journal_get_seqnum(journal.get(), &endSeqNum, nullptr) < 0)
328055713e4SEd Tanous         {
329055713e4SEd Tanous             messages::internalError(asyncResp->res);
330055713e4SEd Tanous             return;
331055713e4SEd Tanous         }
332055713e4SEd Tanous     }
333055713e4SEd Tanous #endif
334055713e4SEd Tanous 
335055713e4SEd Tanous     // Seek to the beginning
336055713e4SEd Tanous     if (sd_journal_seek_head(journal.get()) < 0)
337055713e4SEd Tanous     {
338055713e4SEd Tanous         messages::internalError(asyncResp->res);
339055713e4SEd Tanous         return;
340055713e4SEd Tanous     }
341055713e4SEd Tanous 
342055713e4SEd Tanous     // Get the first entry
343055713e4SEd Tanous     if (sd_journal_next(journal.get()) < 0)
344055713e4SEd Tanous     {
345055713e4SEd Tanous         messages::internalError(asyncResp->res);
346055713e4SEd Tanous         return;
347055713e4SEd Tanous     }
348055713e4SEd Tanous 
349055713e4SEd Tanous     // Get the first sequence number
350055713e4SEd Tanous     uint64_t startSeqNum = 0;
351058f54edSPatrick Williams #if LIBSYSTEMD_VERSION >= 254
352055713e4SEd Tanous     {
353055713e4SEd Tanous         if (sd_journal_get_seqnum(journal.get(), &startSeqNum, nullptr) < 0)
354055713e4SEd Tanous         {
355055713e4SEd Tanous             messages::internalError(asyncResp->res);
356055713e4SEd Tanous             return;
357055713e4SEd Tanous         }
358055713e4SEd Tanous     }
359055713e4SEd Tanous #endif
360055713e4SEd Tanous 
361055713e4SEd Tanous     BMCWEB_LOG_DEBUG("journal Sequence IDs start:{} end:{}", startSeqNum,
362055713e4SEd Tanous                      endSeqNum);
363055713e4SEd Tanous 
364055713e4SEd Tanous     // Add 1 to account for the last entry
365055713e4SEd Tanous     uint64_t totalEntries = endSeqNum - startSeqNum + 1;
366055713e4SEd Tanous     asyncResp->res.jsonValue["Members@odata.count"] = totalEntries;
367055713e4SEd Tanous     if (skip + top < totalEntries)
368b0983db2SEd Tanous     {
36984177a2fSEd Tanous         asyncResp->res.jsonValue["Members@odata.nextLink"] =
37084177a2fSEd Tanous             boost::urls::format(
371b0983db2SEd Tanous                 "/redfish/v1/Managers/{}/LogServices/Journal/Entries?$skip={}",
372b0983db2SEd Tanous                 BMCWEB_REDFISH_MANAGER_URI_NAME, std::to_string(skip + top));
373b0983db2SEd Tanous     }
374055713e4SEd Tanous     uint64_t index = 0;
375055713e4SEd Tanous     if (skip > 0)
376055713e4SEd Tanous     {
377055713e4SEd Tanous         if (sd_journal_next_skip(journal.get(), skip) < 0)
378055713e4SEd Tanous         {
379055713e4SEd Tanous             messages::internalError(asyncResp->res);
380055713e4SEd Tanous             return;
381055713e4SEd Tanous         }
382055713e4SEd Tanous     }
383055713e4SEd Tanous     BMCWEB_LOG_DEBUG("Index was {}", index);
3848274eb11SEd Tanous     readJournalEntries(top, asyncResp, {std::move(journal)});
385b0983db2SEd Tanous }
386b0983db2SEd Tanous 
38784177a2fSEd Tanous inline void handleManagersJournalEntriesLogEntryGet(
38884177a2fSEd Tanous     App& app, const crow::Request& req,
389b0983db2SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
39084177a2fSEd Tanous     const std::string& managerId, const std::string& entryID)
39184177a2fSEd Tanous {
392b0983db2SEd Tanous     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
393b0983db2SEd Tanous     {
394b0983db2SEd Tanous         return;
395b0983db2SEd Tanous     }
396b0983db2SEd Tanous 
397b0983db2SEd Tanous     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
398b0983db2SEd Tanous     {
399b0983db2SEd Tanous         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
400b0983db2SEd Tanous         return;
401b0983db2SEd Tanous     }
402b0983db2SEd Tanous 
403b0983db2SEd Tanous     sd_journal* journalTmp = nullptr;
404b0983db2SEd Tanous     int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
405b0983db2SEd Tanous     if (ret < 0)
406b0983db2SEd Tanous     {
407*7da633f0SEd Tanous         BMCWEB_LOG_ERROR("failed to open journal: {}", ret);
408b0983db2SEd Tanous         messages::internalError(asyncResp->res);
409b0983db2SEd Tanous         return;
410b0983db2SEd Tanous     }
411b0983db2SEd Tanous     std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
412b0983db2SEd Tanous         journalTmp, sd_journal_close);
413b0983db2SEd Tanous     journalTmp = nullptr;
4148274eb11SEd Tanous 
4158274eb11SEd Tanous     std::string cursor;
4168274eb11SEd Tanous     if (!crow::utility::base64Decode(entryID, cursor))
4178274eb11SEd Tanous     {
4188274eb11SEd Tanous         messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
4198274eb11SEd Tanous         return;
4208274eb11SEd Tanous     }
4218274eb11SEd Tanous 
4228274eb11SEd Tanous     // Go to the cursor in the log
4238274eb11SEd Tanous     ret = sd_journal_seek_cursor(journal.get(), cursor.c_str());
424b0983db2SEd Tanous     if (ret < 0)
425b0983db2SEd Tanous     {
4268274eb11SEd Tanous         messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
4278274eb11SEd Tanous         return;
4288274eb11SEd Tanous     }
4298274eb11SEd Tanous 
4308274eb11SEd Tanous     if (sd_journal_next(journal.get()) < 0)
4318274eb11SEd Tanous     {
432b0983db2SEd Tanous         messages::internalError(asyncResp->res);
433b0983db2SEd Tanous         return;
434b0983db2SEd Tanous     }
435055713e4SEd Tanous 
4368274eb11SEd Tanous     ret = sd_journal_test_cursor(journal.get(), cursor.c_str());
4378274eb11SEd Tanous     if (ret == 0)
4388274eb11SEd Tanous     {
4398274eb11SEd Tanous         messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
4408274eb11SEd Tanous         return;
4418274eb11SEd Tanous     }
4428274eb11SEd Tanous     if (ret < 0)
443b0983db2SEd Tanous     {
444b0983db2SEd Tanous         messages::internalError(asyncResp->res);
445b0983db2SEd Tanous         return;
446b0983db2SEd Tanous     }
447055713e4SEd Tanous 
448b0983db2SEd Tanous     nlohmann::json::object_t bmcJournalLogEntry;
4498274eb11SEd Tanous     if (!fillBMCJournalLogEntryJson(journal.get(), bmcJournalLogEntry))
450b0983db2SEd Tanous     {
451b0983db2SEd Tanous         messages::internalError(asyncResp->res);
452b0983db2SEd Tanous         return;
453b0983db2SEd Tanous     }
454b0983db2SEd Tanous     asyncResp->res.jsonValue.update(bmcJournalLogEntry);
455055713e4SEd Tanous }
45684177a2fSEd Tanous 
45784177a2fSEd Tanous inline void requestRoutesBMCJournalLogService(App& app)
45884177a2fSEd Tanous {
45984177a2fSEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Journal/")
46084177a2fSEd Tanous         .privileges(redfish::privileges::getLogService)
46184177a2fSEd Tanous         .methods(boost::beast::http::verb::get)(
46284177a2fSEd Tanous             std::bind_front(handleManagersLogServiceJournalGet, std::ref(app)));
46384177a2fSEd Tanous 
46484177a2fSEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Journal/Entries/")
46584177a2fSEd Tanous         .privileges(redfish::privileges::getLogEntryCollection)
46684177a2fSEd Tanous         .methods(boost::beast::http::verb::get)(std::bind_front(
46784177a2fSEd Tanous             handleManagersJournalLogEntryCollectionGet, std::ref(app)));
46884177a2fSEd Tanous 
46984177a2fSEd Tanous     BMCWEB_ROUTE(
47084177a2fSEd Tanous         app, "/redfish/v1/Managers/<str>/LogServices/Journal/Entries/<str>/")
47184177a2fSEd Tanous         .privileges(redfish::privileges::getLogEntry)
47284177a2fSEd Tanous         .methods(boost::beast::http::verb::get)(std::bind_front(
47384177a2fSEd Tanous             handleManagersJournalEntriesLogEntryGet, std::ref(app)));
474b0983db2SEd Tanous }
475b0983db2SEd Tanous } // namespace redfish
476