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
getJournalMetadata(sd_journal * journal,std::string_view field,std::string_view & contents)23b0983db2SEd Tanous inline int getJournalMetadata(sd_journal* journal, std::string_view 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
33b0983db2SEd Tanous ret = sd_journal_get_data(journal, field.data(), 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
getJournalMetadataInt(sd_journal * journal,std::string_view field,const int & base,long int & contents)44b0983db2SEd Tanous inline int getJournalMetadataInt(sd_journal* journal, std::string_view field,
45b0983db2SEd Tanous const int& base, long int& contents)
46b0983db2SEd Tanous {
47b0983db2SEd Tanous int ret = 0;
48b0983db2SEd Tanous std::string_view metadata;
49b0983db2SEd Tanous // Get the metadata from the requested field of the journal entry
50b0983db2SEd Tanous ret = getJournalMetadata(journal, field, metadata);
51b0983db2SEd Tanous if (ret < 0)
52b0983db2SEd Tanous {
53b0983db2SEd Tanous return ret;
54b0983db2SEd Tanous }
55b0983db2SEd Tanous contents = strtol(metadata.data(), nullptr, base);
56b0983db2SEd Tanous return ret;
57b0983db2SEd Tanous }
58b0983db2SEd Tanous
getEntryTimestamp(sd_journal * journal,std::string & entryTimestamp)59b0983db2SEd Tanous inline bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp)
60b0983db2SEd Tanous {
61b0983db2SEd Tanous int ret = 0;
62b0983db2SEd Tanous uint64_t timestamp = 0;
63b0983db2SEd Tanous ret = sd_journal_get_realtime_usec(journal, ×tamp);
64b0983db2SEd Tanous if (ret < 0)
65b0983db2SEd Tanous {
66b0983db2SEd Tanous BMCWEB_LOG_ERROR("Failed to read entry timestamp: {}", strerror(-ret));
67b0983db2SEd Tanous return false;
68b0983db2SEd Tanous }
69b0983db2SEd Tanous entryTimestamp = redfish::time_utils::getDateTimeUintUs(timestamp);
70b0983db2SEd Tanous return true;
71b0983db2SEd Tanous }
72b0983db2SEd Tanous
fillBMCJournalLogEntryJson(sd_journal * journal,nlohmann::json::object_t & bmcJournalLogEntryJson)73bd79bce8SPatrick Williams inline bool fillBMCJournalLogEntryJson(
74*8274eb11SEd Tanous sd_journal* journal, nlohmann::json::object_t& bmcJournalLogEntryJson)
75b0983db2SEd Tanous {
76*8274eb11SEd Tanous char* cursor = nullptr;
77*8274eb11SEd Tanous int ret = sd_journal_get_cursor(journal, &cursor);
78*8274eb11SEd Tanous if (ret < 0)
79*8274eb11SEd Tanous {
80*8274eb11SEd Tanous return false;
81*8274eb11SEd Tanous }
82*8274eb11SEd Tanous std::unique_ptr<char*> cursorptr = std::make_unique<char*>(cursor);
83*8274eb11SEd Tanous std::string bmcJournalLogEntryID(cursor);
84*8274eb11SEd Tanous
85b0983db2SEd Tanous // Get the Log Entry contents
86b0983db2SEd Tanous std::string message;
87b0983db2SEd Tanous std::string_view syslogID;
88*8274eb11SEd Tanous ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
89b0983db2SEd Tanous if (ret < 0)
90b0983db2SEd Tanous {
91b0983db2SEd Tanous BMCWEB_LOG_DEBUG("Failed to read SYSLOG_IDENTIFIER field: {}",
92b0983db2SEd Tanous strerror(-ret));
93b0983db2SEd Tanous }
94b0983db2SEd Tanous if (!syslogID.empty())
95b0983db2SEd Tanous {
96b0983db2SEd Tanous message += std::string(syslogID) + ": ";
97b0983db2SEd Tanous }
98b0983db2SEd Tanous
99b0983db2SEd Tanous std::string_view msg;
100b0983db2SEd Tanous ret = getJournalMetadata(journal, "MESSAGE", msg);
101b0983db2SEd Tanous if (ret < 0)
102b0983db2SEd Tanous {
103b0983db2SEd Tanous BMCWEB_LOG_ERROR("Failed to read MESSAGE field: {}", strerror(-ret));
104055713e4SEd Tanous return false;
105b0983db2SEd Tanous }
106b0983db2SEd Tanous message += std::string(msg);
107b0983db2SEd Tanous
108b0983db2SEd Tanous // Get the severity from the PRIORITY field
109b0983db2SEd Tanous long int severity = 8; // Default to an invalid priority
110b0983db2SEd Tanous ret = getJournalMetadataInt(journal, "PRIORITY", 10, severity);
111b0983db2SEd Tanous if (ret < 0)
112b0983db2SEd Tanous {
113b0983db2SEd Tanous BMCWEB_LOG_DEBUG("Failed to read PRIORITY field: {}", strerror(-ret));
114b0983db2SEd Tanous }
115b0983db2SEd Tanous
116b0983db2SEd Tanous // Get the Created time from the timestamp
117b0983db2SEd Tanous std::string entryTimeStr;
118b0983db2SEd Tanous if (!getEntryTimestamp(journal, entryTimeStr))
119b0983db2SEd Tanous {
120055713e4SEd Tanous return false;
121b0983db2SEd Tanous }
122b0983db2SEd Tanous
123b0983db2SEd Tanous // Fill in the log entry with the gathered data
124b0983db2SEd Tanous bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
125*8274eb11SEd Tanous
126*8274eb11SEd Tanous std::string entryIdBase64 =
127*8274eb11SEd Tanous crow::utility::base64encode(bmcJournalLogEntryID);
128*8274eb11SEd Tanous
129b0983db2SEd Tanous bmcJournalLogEntryJson["@odata.id"] = boost::urls::format(
130b0983db2SEd Tanous "/redfish/v1/Managers/{}/LogServices/Journal/Entries/{}",
131*8274eb11SEd Tanous BMCWEB_REDFISH_MANAGER_URI_NAME, entryIdBase64);
132b0983db2SEd Tanous bmcJournalLogEntryJson["Name"] = "BMC Journal Entry";
133*8274eb11SEd Tanous bmcJournalLogEntryJson["Id"] = entryIdBase64;
134b0983db2SEd Tanous bmcJournalLogEntryJson["Message"] = std::move(message);
135539d8c6bSEd Tanous bmcJournalLogEntryJson["EntryType"] = log_entry::LogEntryType::Oem;
136b0983db2SEd Tanous log_entry::EventSeverity severityEnum = log_entry::EventSeverity::OK;
137b0983db2SEd Tanous if (severity <= 2)
138b0983db2SEd Tanous {
139b0983db2SEd Tanous severityEnum = log_entry::EventSeverity::Critical;
140b0983db2SEd Tanous }
141b0983db2SEd Tanous else if (severity <= 4)
142b0983db2SEd Tanous {
143b0983db2SEd Tanous severityEnum = log_entry::EventSeverity::Warning;
144b0983db2SEd Tanous }
145b0983db2SEd Tanous
146b0983db2SEd Tanous bmcJournalLogEntryJson["Severity"] = severityEnum;
147b0983db2SEd Tanous bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry";
148b0983db2SEd Tanous bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr);
149055713e4SEd Tanous return true;
150b0983db2SEd Tanous }
151b0983db2SEd Tanous
handleManagersLogServiceJournalGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)15284177a2fSEd Tanous inline void handleManagersLogServiceJournalGet(
15384177a2fSEd Tanous App& app, const crow::Request& req,
154b0983db2SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
15584177a2fSEd Tanous const std::string& managerId)
15684177a2fSEd Tanous {
157b0983db2SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp))
158b0983db2SEd Tanous {
159b0983db2SEd Tanous return;
160b0983db2SEd Tanous }
161b0983db2SEd Tanous
162b0983db2SEd Tanous if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
163b0983db2SEd Tanous {
164b0983db2SEd Tanous messages::resourceNotFound(asyncResp->res, "Manager", managerId);
165b0983db2SEd Tanous return;
166b0983db2SEd Tanous }
167b0983db2SEd Tanous
16884177a2fSEd Tanous asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
169b0983db2SEd Tanous asyncResp->res.jsonValue["@odata.id"] =
170b0983db2SEd Tanous boost::urls::format("/redfish/v1/Managers/{}/LogServices/Journal",
171b0983db2SEd Tanous BMCWEB_REDFISH_MANAGER_URI_NAME);
172b0983db2SEd Tanous asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
173b0983db2SEd Tanous asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
174b0983db2SEd Tanous asyncResp->res.jsonValue["Id"] = "Journal";
175b0983db2SEd Tanous asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
176b0983db2SEd Tanous
177b0983db2SEd Tanous std::pair<std::string, std::string> redfishDateTimeOffset =
178b0983db2SEd Tanous redfish::time_utils::getDateTimeOffsetNow();
179b0983db2SEd Tanous asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
180b0983db2SEd Tanous asyncResp->res.jsonValue["DateTimeLocalOffset"] =
181b0983db2SEd Tanous redfishDateTimeOffset.second;
182b0983db2SEd Tanous
183b0983db2SEd Tanous asyncResp->res.jsonValue["Entries"]["@odata.id"] = boost::urls::format(
184b0983db2SEd Tanous "/redfish/v1/Managers/{}/LogServices/Journal/Entries",
185b0983db2SEd Tanous BMCWEB_REDFISH_MANAGER_URI_NAME);
186b0983db2SEd Tanous }
187b0983db2SEd Tanous
188055713e4SEd Tanous struct JournalReadState
189055713e4SEd Tanous {
190055713e4SEd Tanous std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal;
191055713e4SEd Tanous };
192055713e4SEd Tanous
readJournalEntries(uint64_t topEntryCount,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,JournalReadState && readState)193bd79bce8SPatrick Williams inline void readJournalEntries(
194bd79bce8SPatrick Williams uint64_t topEntryCount, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
195055713e4SEd Tanous JournalReadState&& readState)
196055713e4SEd Tanous {
197055713e4SEd Tanous nlohmann::json& logEntry = asyncResp->res.jsonValue["Members"];
198055713e4SEd Tanous nlohmann::json::array_t* logEntryArray =
199055713e4SEd Tanous logEntry.get_ptr<nlohmann::json::array_t*>();
200055713e4SEd Tanous if (logEntryArray == nullptr)
201055713e4SEd Tanous {
202055713e4SEd Tanous messages::internalError(asyncResp->res);
203055713e4SEd Tanous return;
204055713e4SEd Tanous }
205055713e4SEd Tanous
206055713e4SEd Tanous // The Journal APIs unfortunately do blocking calls to the filesystem, and
207055713e4SEd Tanous // can be somewhat expensive. Short of creating our own io_uring based
208055713e4SEd Tanous // implementation of sd-journal, which would be difficult, the best thing we
209055713e4SEd Tanous // can do is to only parse a certain number of entries at a time. The
210055713e4SEd Tanous // current chunk size is selected arbitrarily to ensure that we're not
211055713e4SEd Tanous // trying to process thousands of entries at the same time.
212055713e4SEd Tanous // The implementation will process the number of entries, then return
213055713e4SEd Tanous // control to the io_context to let other operations continue.
214055713e4SEd Tanous size_t segmentCountRemaining = 10;
215055713e4SEd Tanous
216055713e4SEd Tanous // Reset the unique ID on the first entry
217055713e4SEd Tanous for (uint64_t entryCount = logEntryArray->size();
218055713e4SEd Tanous entryCount < topEntryCount; entryCount++)
219055713e4SEd Tanous {
220055713e4SEd Tanous if (segmentCountRemaining == 0)
221055713e4SEd Tanous {
222055713e4SEd Tanous boost::asio::post(crow::connections::systemBus->get_io_context(),
223055713e4SEd Tanous [asyncResp, topEntryCount,
224055713e4SEd Tanous readState = std::move(readState)]() mutable {
225055713e4SEd Tanous readJournalEntries(topEntryCount, asyncResp,
226055713e4SEd Tanous std::move(readState));
227055713e4SEd Tanous });
228055713e4SEd Tanous return;
229055713e4SEd Tanous }
230055713e4SEd Tanous
231055713e4SEd Tanous nlohmann::json::object_t bmcJournalLogEntry;
232*8274eb11SEd Tanous if (!fillBMCJournalLogEntryJson(readState.journal.get(),
233055713e4SEd Tanous bmcJournalLogEntry))
234055713e4SEd Tanous {
235055713e4SEd Tanous messages::internalError(asyncResp->res);
236055713e4SEd Tanous return;
237055713e4SEd Tanous }
238055713e4SEd Tanous logEntryArray->emplace_back(std::move(bmcJournalLogEntry));
239055713e4SEd Tanous
240*8274eb11SEd Tanous int ret = sd_journal_next(readState.journal.get());
241055713e4SEd Tanous if (ret < 0)
242055713e4SEd Tanous {
243055713e4SEd Tanous messages::internalError(asyncResp->res);
244055713e4SEd Tanous return;
245055713e4SEd Tanous }
246055713e4SEd Tanous if (ret == 0)
247055713e4SEd Tanous {
248055713e4SEd Tanous break;
249055713e4SEd Tanous }
250055713e4SEd Tanous segmentCountRemaining--;
251055713e4SEd Tanous }
252055713e4SEd Tanous }
253055713e4SEd Tanous
handleManagersJournalLogEntryCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)25484177a2fSEd Tanous inline void handleManagersJournalLogEntryCollectionGet(
25584177a2fSEd Tanous App& app, const crow::Request& req,
256b0983db2SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
25784177a2fSEd Tanous const std::string& managerId)
25884177a2fSEd Tanous {
259b0983db2SEd Tanous query_param::QueryCapabilities capabilities = {
260b0983db2SEd Tanous .canDelegateTop = true,
261b0983db2SEd Tanous .canDelegateSkip = true,
262b0983db2SEd Tanous };
263b0983db2SEd Tanous query_param::Query delegatedQuery;
26484177a2fSEd Tanous if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp,
26584177a2fSEd Tanous delegatedQuery, capabilities))
266b0983db2SEd Tanous {
267b0983db2SEd Tanous return;
268b0983db2SEd Tanous }
269b0983db2SEd Tanous
270b0983db2SEd Tanous if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
271b0983db2SEd Tanous {
272b0983db2SEd Tanous messages::resourceNotFound(asyncResp->res, "Manager", managerId);
273b0983db2SEd Tanous return;
274b0983db2SEd Tanous }
275b0983db2SEd Tanous
276b0983db2SEd Tanous size_t skip = delegatedQuery.skip.value_or(0);
277b0983db2SEd Tanous size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
278b0983db2SEd Tanous
279b0983db2SEd Tanous // Collections don't include the static data added by SubRoute
280b0983db2SEd Tanous // because it has a duplicate entry for members
281b0983db2SEd Tanous asyncResp->res.jsonValue["@odata.type"] =
282b0983db2SEd Tanous "#LogEntryCollection.LogEntryCollection";
283b0983db2SEd Tanous asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
284b0983db2SEd Tanous "/redfish/v1/Managers/{}/LogServices/Journal/Entries",
285b0983db2SEd Tanous BMCWEB_REDFISH_MANAGER_URI_NAME);
286b0983db2SEd Tanous asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
287b0983db2SEd Tanous asyncResp->res.jsonValue["Description"] =
288b0983db2SEd Tanous "Collection of BMC Journal Entries";
289055713e4SEd Tanous asyncResp->res.jsonValue["Members"] = nlohmann::json::array_t();
290b0983db2SEd Tanous
291b0983db2SEd Tanous // Go through the journal and use the timestamp to create a
292b0983db2SEd Tanous // unique ID for each entry
293b0983db2SEd Tanous sd_journal* journalTmp = nullptr;
294b0983db2SEd Tanous int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
295b0983db2SEd Tanous if (ret < 0)
296b0983db2SEd Tanous {
297b0983db2SEd Tanous BMCWEB_LOG_ERROR("failed to open journal: {}", strerror(-ret));
298b0983db2SEd Tanous messages::internalError(asyncResp->res);
299b0983db2SEd Tanous return;
300b0983db2SEd Tanous }
301055713e4SEd Tanous
302b0983db2SEd Tanous std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
303b0983db2SEd Tanous journalTmp, sd_journal_close);
304b0983db2SEd Tanous journalTmp = nullptr;
305b0983db2SEd Tanous
306055713e4SEd Tanous // Seek to the end
307055713e4SEd Tanous if (sd_journal_seek_tail(journal.get()) < 0)
308b0983db2SEd Tanous {
309b0983db2SEd Tanous messages::internalError(asyncResp->res);
310b0983db2SEd Tanous return;
311b0983db2SEd Tanous }
312055713e4SEd Tanous
313055713e4SEd Tanous // Get the last entry
314055713e4SEd Tanous if (sd_journal_previous(journal.get()) < 0)
315055713e4SEd Tanous {
316055713e4SEd Tanous messages::internalError(asyncResp->res);
317055713e4SEd Tanous return;
318b0983db2SEd Tanous }
319055713e4SEd Tanous
320055713e4SEd Tanous // Get the last sequence number
321055713e4SEd Tanous uint64_t endSeqNum = 0;
322058f54edSPatrick Williams #if LIBSYSTEMD_VERSION >= 254
323055713e4SEd Tanous {
324055713e4SEd Tanous if (sd_journal_get_seqnum(journal.get(), &endSeqNum, nullptr) < 0)
325055713e4SEd Tanous {
326055713e4SEd Tanous messages::internalError(asyncResp->res);
327055713e4SEd Tanous return;
328055713e4SEd Tanous }
329055713e4SEd Tanous }
330055713e4SEd Tanous #endif
331055713e4SEd Tanous
332055713e4SEd Tanous // Seek to the beginning
333055713e4SEd Tanous if (sd_journal_seek_head(journal.get()) < 0)
334055713e4SEd Tanous {
335055713e4SEd Tanous messages::internalError(asyncResp->res);
336055713e4SEd Tanous return;
337055713e4SEd Tanous }
338055713e4SEd Tanous
339055713e4SEd Tanous // Get the first entry
340055713e4SEd Tanous if (sd_journal_next(journal.get()) < 0)
341055713e4SEd Tanous {
342055713e4SEd Tanous messages::internalError(asyncResp->res);
343055713e4SEd Tanous return;
344055713e4SEd Tanous }
345055713e4SEd Tanous
346055713e4SEd Tanous // Get the first sequence number
347055713e4SEd Tanous uint64_t startSeqNum = 0;
348058f54edSPatrick Williams #if LIBSYSTEMD_VERSION >= 254
349055713e4SEd Tanous {
350055713e4SEd Tanous if (sd_journal_get_seqnum(journal.get(), &startSeqNum, nullptr) < 0)
351055713e4SEd Tanous {
352055713e4SEd Tanous messages::internalError(asyncResp->res);
353055713e4SEd Tanous return;
354055713e4SEd Tanous }
355055713e4SEd Tanous }
356055713e4SEd Tanous #endif
357055713e4SEd Tanous
358055713e4SEd Tanous BMCWEB_LOG_DEBUG("journal Sequence IDs start:{} end:{}", startSeqNum,
359055713e4SEd Tanous endSeqNum);
360055713e4SEd Tanous
361055713e4SEd Tanous // Add 1 to account for the last entry
362055713e4SEd Tanous uint64_t totalEntries = endSeqNum - startSeqNum + 1;
363055713e4SEd Tanous asyncResp->res.jsonValue["Members@odata.count"] = totalEntries;
364055713e4SEd Tanous if (skip + top < totalEntries)
365b0983db2SEd Tanous {
36684177a2fSEd Tanous asyncResp->res.jsonValue["Members@odata.nextLink"] =
36784177a2fSEd Tanous boost::urls::format(
368b0983db2SEd Tanous "/redfish/v1/Managers/{}/LogServices/Journal/Entries?$skip={}",
369b0983db2SEd Tanous BMCWEB_REDFISH_MANAGER_URI_NAME, std::to_string(skip + top));
370b0983db2SEd Tanous }
371055713e4SEd Tanous uint64_t index = 0;
372055713e4SEd Tanous if (skip > 0)
373055713e4SEd Tanous {
374055713e4SEd Tanous if (sd_journal_next_skip(journal.get(), skip) < 0)
375055713e4SEd Tanous {
376055713e4SEd Tanous messages::internalError(asyncResp->res);
377055713e4SEd Tanous return;
378055713e4SEd Tanous }
379055713e4SEd Tanous }
380055713e4SEd Tanous BMCWEB_LOG_DEBUG("Index was {}", index);
381*8274eb11SEd Tanous readJournalEntries(top, asyncResp, {std::move(journal)});
382b0983db2SEd Tanous }
383b0983db2SEd Tanous
handleManagersJournalEntriesLogEntryGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId,const std::string & entryID)38484177a2fSEd Tanous inline void handleManagersJournalEntriesLogEntryGet(
38584177a2fSEd Tanous App& app, const crow::Request& req,
386b0983db2SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
38784177a2fSEd Tanous const std::string& managerId, const std::string& entryID)
38884177a2fSEd Tanous {
389b0983db2SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp))
390b0983db2SEd Tanous {
391b0983db2SEd Tanous return;
392b0983db2SEd Tanous }
393b0983db2SEd Tanous
394b0983db2SEd Tanous if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
395b0983db2SEd Tanous {
396b0983db2SEd Tanous messages::resourceNotFound(asyncResp->res, "Manager", managerId);
397b0983db2SEd Tanous return;
398b0983db2SEd Tanous }
399b0983db2SEd Tanous
400b0983db2SEd Tanous sd_journal* journalTmp = nullptr;
401b0983db2SEd Tanous int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
402b0983db2SEd Tanous if (ret < 0)
403b0983db2SEd Tanous {
404b0983db2SEd Tanous BMCWEB_LOG_ERROR("failed to open journal: {}", strerror(-ret));
405b0983db2SEd Tanous messages::internalError(asyncResp->res);
406b0983db2SEd Tanous return;
407b0983db2SEd Tanous }
408b0983db2SEd Tanous std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
409b0983db2SEd Tanous journalTmp, sd_journal_close);
410b0983db2SEd Tanous journalTmp = nullptr;
411*8274eb11SEd Tanous
412*8274eb11SEd Tanous std::string cursor;
413*8274eb11SEd Tanous if (!crow::utility::base64Decode(entryID, cursor))
414*8274eb11SEd Tanous {
415*8274eb11SEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
416*8274eb11SEd Tanous return;
417*8274eb11SEd Tanous }
418*8274eb11SEd Tanous
419*8274eb11SEd Tanous // Go to the cursor in the log
420*8274eb11SEd Tanous ret = sd_journal_seek_cursor(journal.get(), cursor.c_str());
421b0983db2SEd Tanous if (ret < 0)
422b0983db2SEd Tanous {
423*8274eb11SEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
424*8274eb11SEd Tanous return;
425*8274eb11SEd Tanous }
426*8274eb11SEd Tanous
427*8274eb11SEd Tanous if (sd_journal_next(journal.get()) < 0)
428*8274eb11SEd Tanous {
429b0983db2SEd Tanous messages::internalError(asyncResp->res);
430b0983db2SEd Tanous return;
431b0983db2SEd Tanous }
432055713e4SEd Tanous
433*8274eb11SEd Tanous ret = sd_journal_test_cursor(journal.get(), cursor.c_str());
434*8274eb11SEd Tanous if (ret == 0)
435*8274eb11SEd Tanous {
436*8274eb11SEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
437*8274eb11SEd Tanous return;
438*8274eb11SEd Tanous }
439*8274eb11SEd Tanous if (ret < 0)
440b0983db2SEd Tanous {
441b0983db2SEd Tanous messages::internalError(asyncResp->res);
442b0983db2SEd Tanous return;
443b0983db2SEd Tanous }
444055713e4SEd Tanous
445b0983db2SEd Tanous nlohmann::json::object_t bmcJournalLogEntry;
446*8274eb11SEd Tanous if (!fillBMCJournalLogEntryJson(journal.get(), bmcJournalLogEntry))
447b0983db2SEd Tanous {
448b0983db2SEd Tanous messages::internalError(asyncResp->res);
449b0983db2SEd Tanous return;
450b0983db2SEd Tanous }
451b0983db2SEd Tanous asyncResp->res.jsonValue.update(bmcJournalLogEntry);
452055713e4SEd Tanous }
45384177a2fSEd Tanous
requestRoutesBMCJournalLogService(App & app)45484177a2fSEd Tanous inline void requestRoutesBMCJournalLogService(App& app)
45584177a2fSEd Tanous {
45684177a2fSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Journal/")
45784177a2fSEd Tanous .privileges(redfish::privileges::getLogService)
45884177a2fSEd Tanous .methods(boost::beast::http::verb::get)(
45984177a2fSEd Tanous std::bind_front(handleManagersLogServiceJournalGet, std::ref(app)));
46084177a2fSEd Tanous
46184177a2fSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Journal/Entries/")
46284177a2fSEd Tanous .privileges(redfish::privileges::getLogEntryCollection)
46384177a2fSEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front(
46484177a2fSEd Tanous handleManagersJournalLogEntryCollectionGet, std::ref(app)));
46584177a2fSEd Tanous
46684177a2fSEd Tanous BMCWEB_ROUTE(
46784177a2fSEd Tanous app, "/redfish/v1/Managers/<str>/LogServices/Journal/Entries/<str>/")
46884177a2fSEd Tanous .privileges(redfish::privileges::getLogEntry)
46984177a2fSEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front(
47084177a2fSEd Tanous handleManagersJournalEntriesLogEntryGet, std::ref(app)));
471b0983db2SEd Tanous }
472b0983db2SEd Tanous } // namespace redfish
473