blob: 78b72e8d029d9a7165e0297e98f0e0f7938d114a [file] [log] [blame]
Ben Tynerb1ebfcb2020-05-08 18:52:48 -05001#include <unistd.h>
2
Ben Tynerb797b3e2020-06-29 10:12:05 -05003#include <attn/attn_logging.hpp>
Ben Tynerf5210bb2021-01-05 12:58:10 -06004#include <attn/pel/pel_minimal.hpp>
Ben Tynerb1ebfcb2020-05-08 18:52:48 -05005#include <phosphor-logging/log.hpp>
Ben Tynerf5210bb2021-01-05 12:58:10 -06006
Ben Tynerb1ebfcb2020-05-08 18:52:48 -05007namespace attn
8{
9
Ben Tynerf5210bb2021-01-05 12:58:10 -060010/** @brief Journal entry of type INFO using phosphor logging */
Ben Tynerb1ebfcb2020-05-08 18:52:48 -050011template <>
12void trace<INFO>(const char* i_message)
13{
14 phosphor::logging::log<phosphor::logging::level::INFO>(i_message);
15}
16
Ben Tynerf5210bb2021-01-05 12:58:10 -060017/** @brief Tuple containing information about ffdc files */
18using FFDCTuple =
19 std::tuple<util::FFDCFormat, uint8_t, uint8_t, sdbusplus::message::unix_fd>;
20
21/** @brief Gather messages from the journal */
22std::vector<std::string> sdjGetMessages(const std::string& field,
23 const std::string& fieldValue,
24 unsigned int max);
25
26/**
27 * Create FFDCTuple objects corresponding to the specified FFDC files.
28 *
29 * The D-Bus method to create an error log requires a vector of tuples to
30 * pass in the FFDC file information.
31 *
32 * @param files - FFDC files
33 * @return vector of FFDCTuple objects
34 */
35std::vector<FFDCTuple>
36 createFFDCTuples(const std::vector<util::FFDCFile>& files)
37{
38 std::vector<FFDCTuple> ffdcTuples{};
Zane Shelleya79f6c82021-01-12 16:38:49 -060039 util::transformFFDC(files, ffdcTuples);
Ben Tynerf5210bb2021-01-05 12:58:10 -060040
41 return ffdcTuples;
42}
43
44/**
45 * @brief Create an FFDCFile object containing raw data
46 *
47 * Throws an exception if an error occurs.
48 *
49 * @param i_buffer - raw data to add to ffdc faw data file
50 * @param i_size - size of the raw data
51 * @return FFDCFile object
52 */
53util::FFDCFile createFFDCRawFile(void* i_buffer, size_t i_size)
54{
55 util::FFDCFile file{util::FFDCFormat::Custom};
56
57 // Write buffer to file and then reset file description file offset
58 int fd = file.getFileDescriptor();
59 write(fd, static_cast<char*>(i_buffer), i_size);
60 lseek(fd, 0, SEEK_SET);
61
62 return file;
63}
64
65/**
66 * @brief Create an FFDCFile object containing the specified lines of text data
67 *
68 * Throws an exception if an error occurs.
69 *
70 * @param lines - lines of text data to write to file
71 * @return FFDCFile object
72 */
73util::FFDCFile createFFDCTraceFile(const std::vector<std::string>& lines)
74{
75 // Create FFDC file of type Text
76 util::FFDCFile file{util::FFDCFormat::Text};
77 int fd = file.getFileDescriptor();
78
79 // Write FFDC lines to file
80 std::string buffer;
81 for (const std::string& line : lines)
82 {
83 // Copy line to buffer. Add newline if necessary.
84 buffer = line;
85 if (line.empty() || (line.back() != '\n'))
86 {
87 buffer += '\n';
88 }
89
90 // write buffer to file
91 write(fd, buffer.c_str(), buffer.size());
92 }
93
94 // Seek to beginning of file so error logging system can read data
95 lseek(fd, 0, SEEK_SET);
96
97 return file;
98}
99
100/**
101 * Create FDDC files from journal messages of relevant executables
102 *
103 * Parse the system journal looking for log entries created by the executables
104 * of interest for logging. For each of these entries create a ffdc trace file
105 * that will be used to create ffdc log entries. These files will be pushed
106 * onto the stack of ffdc files.
107 *
108 * @param i_files - vector of ffdc files that will become log entries
109 */
110void createFFDCTraceFiles(std::vector<util::FFDCFile>& i_files)
111{
112 // Executables of interest
113 std::vector<std::string> executables{"openpower-hw-diags"};
114
115 for (const std::string& executable : executables)
116 {
117 try
118 {
119 // get journal messages
120 std::vector<std::string> messages =
121 sdjGetMessages("SYSLOG_IDENTIFIER", executable, 30);
122
123 // Create FFDC file containing the journal messages
124 if (!messages.empty())
125 {
126 i_files.emplace_back(createFFDCTraceFile(messages));
127 }
128 }
129 catch (const std::exception& e)
130 {
131 std::stringstream ss;
132 ss << "createFFDCFiles: " << e.what();
133 trace<level::INFO>(ss.str().c_str());
134 }
135 }
136}
137
138/**
139 * Create FFDCFile objects containing debug data to store in the error log.
140 *
141 * If an error occurs, the error is written to the journal but an exception
142 * is not thrown.
143 *
144 * @param i_buffer - raw data (if creating raw dump ffdc entry in log)
145 * @return vector of FFDCFile objects
146 */
147std::vector<util::FFDCFile> createFFDCFiles(char* i_buffer = nullptr,
148 size_t i_size = 0)
149{
150 std::vector<util::FFDCFile> files{};
151
152 // Create raw dump file
153 if ((nullptr != i_buffer) && (0 != i_size))
154 {
155 files.emplace_back(createFFDCRawFile(i_buffer, i_size));
156 }
157
158 // Create trace dump file
159 createFFDCTraceFiles(files);
160
161 return files;
162}
163
164/**
165 * Get file descriptor of exisitng PEL
166 *
167 * The backend logging code will search for a PEL having the provided PEL ID
168 * and return a file descriptor to a file containing this PEL's raw PEL data.
169 *
170 * @param i_pelid - the PEL ID
171 * @return file descriptor of file containing the raw PEL data
172 */
173int getPelFd(uint32_t i_pelId)
174{
175 // GetPEL returns file descriptor (int)
176 int fd = -1;
177
Ben Tynerf5210bb2021-01-05 12:58:10 -0600178 // Get the PEL file descriptor
179 try
180 {
Ben Tynerdc5b0ff2021-01-14 14:01:39 -0600181 // Sdbus call specifics
182 constexpr auto service = "xyz.openbmc_project.Logging";
183 constexpr auto path = "/xyz/openbmc_project/logging";
184 constexpr auto interface = "org.open_power.Logging.PEL";
185 constexpr auto function = "GetPEL";
186
Ben Tynerf5210bb2021-01-05 12:58:10 -0600187 auto bus = sdbusplus::bus::new_default_system();
188 auto method = bus.new_method_call(service, path, interface, function);
189 method.append(i_pelId);
190
191 auto resp = bus.call(method);
192
193 sdbusplus::message::unix_fd msgFd;
194 resp.read(msgFd);
195 fd = dup(msgFd); // -1 if not found
196 }
197 catch (const sdbusplus::exception::SdBusError& e)
198 {
199 std::stringstream ss;
200 ss << "getPelFd: " << e.what();
201 trace<level::INFO>(ss.str().c_str());
202 }
203
204 // File descriptor or -1 if not found or call failed
205 return fd;
206}
207
208/**
209 * Create a PEL for the specified event type
210 *
211 * The additional data provided in the map will be placed in a user data
212 * section of the PEL and may additionally contain key words to trigger
213 * certain behaviors by the backend logging code. Each set of data described
214 * in the vector of ffdc data will be placed in additional user data sections.
215 *
216 * @param i_event - the event type
217 * @param i_additional - map of additional data
218 * @param 9_ffdc - vector of ffdc data
219 * @return The created PEL's platform log-id
220 */
221uint32_t createPel(std::string i_event,
222 std::map<std::string, std::string> i_additional,
223 std::vector<util::FFDCTuple> i_ffdc)
224{
225 // CreatePELWithFFDCFiles returns log-id and platform log-id
226 std::tuple<uint32_t, uint32_t> pelResp = {0, 0};
227
Ben Tynerf5210bb2021-01-05 12:58:10 -0600228 // Need to provide pid when using create or create-with-ffdc methods
229 i_additional.emplace("_PID", std::to_string(getpid()));
230
231 // Create the PEL
232 try
233 {
Ben Tynerdc5b0ff2021-01-14 14:01:39 -0600234 // Sdbus call specifics
235 constexpr auto level = "xyz.openbmc_project.Logging.Entry.Level.Error";
236 constexpr auto service = "xyz.openbmc_project.Logging";
237 constexpr auto path = "/xyz/openbmc_project/logging";
238 constexpr auto interface = "org.open_power.Logging.PEL";
239 constexpr auto function = "CreatePELWithFFDCFiles";
240
Ben Tynerf5210bb2021-01-05 12:58:10 -0600241 auto bus = sdbusplus::bus::new_default_system();
242 auto method = bus.new_method_call(service, path, interface, function);
243 method.append(i_event, level, i_additional, i_ffdc);
244
245 auto resp = bus.call(method);
246
247 resp.read(pelResp);
248 }
249 catch (const sdbusplus::exception::SdBusError& e)
250 {
251 std::stringstream ss;
252 ss << "createPel: " << e.what();
253 trace<level::INFO>(ss.str().c_str());
254 }
255
256 // pelResp<0> == log-id, pelResp<1> = platform log-id
257 return std::get<1>(pelResp);
258}
259
260/*
261 * Create a PEL from raw PEL data
262 *
263 * The backend logging code will create a PEL based on the specified PEL data.
264 *
265 * @param i_buffer - buffer containing a raw PEL
266 */
267void createPelRaw(std::vector<uint8_t>& i_buffer)
268{
Ben Tynerf5210bb2021-01-05 12:58:10 -0600269 // Create FFDC file from buffer data
270 util::FFDCFile pelFile{util::FFDCFormat::Text};
271 auto fd = pelFile.getFileDescriptor();
272
273 write(fd, i_buffer.data(), i_buffer.size());
274 lseek(fd, 0, SEEK_SET);
275
276 auto filePath = pelFile.getPath();
277
278 // Additional data for log
279 std::map<std::string, std::string> additional;
280 additional.emplace("RAWPEL", filePath.string());
281 additional.emplace("_PID", std::to_string(getpid()));
282
283 // Create the PEL
284 try
285 {
Ben Tynerdc5b0ff2021-01-14 14:01:39 -0600286 // Sdbus call specifics
287 constexpr auto pelEvent = "xyz.open_power.Attn.Error.Terminate";
288 constexpr auto level = "xyz.openbmc_project.Logging.Entry.Level.Error";
289 constexpr auto service = "xyz.openbmc_project.Logging";
290 constexpr auto path = "/xyz/openbmc_project/logging";
291 constexpr auto interface = "xyz.openbmc_project.Logging.Create";
292 constexpr auto function = "Create";
293
Ben Tynerf5210bb2021-01-05 12:58:10 -0600294 auto bus = sdbusplus::bus::new_default_system();
295 auto method = bus.new_method_call(service, path, interface, function);
Ben Tynerdc5b0ff2021-01-14 14:01:39 -0600296 method.append(pelEvent, level, additional);
297
Ben Tynerf5210bb2021-01-05 12:58:10 -0600298 bus.call_noreply(method);
299 }
300 catch (const sdbusplus::exception::SdBusError& e)
301 {
302 std::stringstream ss;
303 ss << "createPelRaw: " << e.what();
304 trace<level::INFO>(ss.str().c_str());
305 }
306}
307
308/**
309 * Create a PEL from an existing PEL
310 *
311 * Create a new PEL based on the specified raw PEL and submit the new PEL
312 * to the backend logging code as a raw PEL. Note that additional data map
313 * here contains data to be committed to the PEL and it can also be used to
314 * create the PEL as it contains needed information.
315 *
316 * @param i_buffer - buffer containing a raw PEL
317 * @param i_additional - additional data to be added to the new PEL
318 */
319void createPelCustom(std::vector<uint8_t>& i_rawPel,
320 std::map<std::string, std::string> i_additional)
321{
322 // create PEL object from buffer
323 auto tiPel = std::make_unique<pel::PelMinimal>(i_rawPel);
324
325 // The additional data contains the TI info as well as the value for the
326 // subystem that provided the TI info. Get the subystem from additional
327 // data and then populate the prmary SRC and SRC words for the custom PEL
328 // based on the sybsystem's TI info.
329 uint8_t subsystem = std::stoi(i_additional["Subsystem"]);
330 tiPel->setSubsystem(subsystem);
331
332 if (static_cast<uint8_t>(pel::SubsystemID::hypervisor) == subsystem)
333 {
334 // populate hypervisor SRC words
335 tiPel->setSrcWords(std::array<uint32_t, pel::numSrcWords>{
336 (uint32_t)std::stoul(i_additional["0x10 SRC Word 12"], 0, 16),
337 (uint32_t)std::stoul(i_additional["0x14 SRC Word 13"], 0, 16),
338 (uint32_t)std::stoul(i_additional["0x18 SRC Word 14"], 0, 16),
339 (uint32_t)std::stoul(i_additional["0x1c SRC Word 15"], 0, 16),
340 (uint32_t)std::stoul(i_additional["0x20 SRC Word 16"], 0, 16),
341 (uint32_t)std::stoul(i_additional["0x24 SRC Word 17"], 0, 16),
342 (uint32_t)std::stoul(i_additional["0x28 SRC Word 18"], 0, 16),
343 (uint32_t)std::stoul(i_additional["0x2c SRC Word 19"], 0, 16)});
344
345 // populate hypervisor primary SRC
346 std::array<char, pel::asciiStringSize> srcChars{'0'};
347 std::string srcString = i_additional["SrcAscii"];
348 srcString.copy(srcChars.data(),
349 std::min(srcString.size(), pel::asciiStringSize), 0);
350 tiPel->setAsciiString(srcChars);
351 }
352 else
353 {
354 // Populate hostboot SRC words - note HB word 0 from the shared info
355 // data (additional data "0x10 HB Word") is reflected in the PEL as
356 // "reason code" so we zero it here. Also note that the first word
357 // in this group of words starts at word 0 and word 1 does not exits.
358 tiPel->setSrcWords(std::array<uint32_t, pel::numSrcWords>{
359 (uint32_t)0x00000000,
360 (uint32_t)std::stoul(i_additional["0x14 HB Word 2"], 0, 16),
361 (uint32_t)std::stoul(i_additional["0x18 HB Word 3"], 0, 16),
362 (uint32_t)std::stoul(i_additional["0x1c HB Word 4"], 0, 16),
363 (uint32_t)std::stoul(i_additional["0x20 HB Word 5"], 0, 16),
364 (uint32_t)std::stoul(i_additional["0x24 HB Word 6"], 0, 16),
365 (uint32_t)std::stoul(i_additional["0x28 HB Word 7"], 0, 16),
366 (uint32_t)std::stoul(i_additional["0x2c HB Word 8"], 0, 16)});
367
368 // populate hostboot primary SRC
369 std::array<char, pel::asciiStringSize> srcChars{'0'};
370 std::string srcString = i_additional["0x30 error_data"];
371 srcString.copy(srcChars.data(),
372 std::min(srcString.size(), pel::asciiStringSize), 0);
373 tiPel->setAsciiString(srcChars);
374 }
375
376 // set severity, event type and action flags
377 tiPel->setSeverity(static_cast<uint8_t>(pel::Severity::termination));
378 tiPel->setType(static_cast<uint8_t>(pel::EventType::na));
379 tiPel->setAction(static_cast<uint16_t>(pel::ActionFlags::service |
380 pel::ActionFlags::report |
381 pel::ActionFlags::call));
382
383 // The raw PEL that we used as the basis for this custom PEL contains the
384 // attention handler trace data and does not needed to be in this PEL so
385 // we remove it here.
386 tiPel->setSectionCount(tiPel->getSectionCount() - 1);
387
388 // Update the raw PEL with the new custom PEL data
389 tiPel->raw(i_rawPel);
390
391 // create PEL from raw data
392 createPelRaw(i_rawPel);
393}
394
395/**
396 * Log an event handled by the attention handler
397 *
398 * Basic (non TI) events will generate a standard message-registry based PEL
399 *
400 * TI events will create two PEL's. One PEL will be informational and will
401 * contain trace information relevent to attention handler. The second PEL
402 * will be specific to the TI type (including the primary SRC) and will be
403 * based off of the TI information provided to the attention handler through
404 * shared TI info data area.
405 *
406 * @param i_event - The event type
407 * @param i_additional - Additional PEL data
408 * @param i_ffdc - FFDC PEL data
409 */
410void event(EventType i_event, std::map<std::string, std::string>& i_additional,
411 const std::vector<util::FFDCFile>& i_ffdc)
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500412{
413 bool eventValid = false; // assume no event created
Ben Tynerf5210bb2021-01-05 12:58:10 -0600414 bool tiEvent = false; // assume not a terminate event
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500415
416 std::string eventName;
417
418 switch (i_event)
419 {
420 case EventType::Checkstop:
421 eventName = "org.open_power.HwDiags.Error.Checkstop";
422 eventValid = true;
423 break;
424 case EventType::Terminate:
425 eventName = "org.open_power.Attn.Error.Terminate";
426 eventValid = true;
Ben Tynerf5210bb2021-01-05 12:58:10 -0600427 tiEvent = true;
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500428 break;
429 case EventType::Vital:
430 eventName = "org.open_power.Attn.Error.Vital";
431 eventValid = true;
432 break;
433 case EventType::HwDiagsFail:
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500434 case EventType::AttentionFail:
435 eventName = "org.open_power.Attn.Error.Fail";
436 eventValid = true;
437 break;
438 default:
439 eventValid = false;
440 break;
441 }
442
443 if (true == eventValid)
444 {
Ben Tynerf5210bb2021-01-05 12:58:10 -0600445 // Create PEL with additional data and FFDC data. The newly created
446 // PEL's platform log-id will be returned.
447 auto pelId =
448 createPel(eventName, i_additional, createFFDCTuples(i_ffdc));
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500449
Ben Tynerf5210bb2021-01-05 12:58:10 -0600450 // If this is a TI event we will create an additional PEL that is
451 // specific to the subsystem that generated the TI.
452 if (true == tiEvent)
453 {
454 // get file descriptor and size of information PEL
455 int pelFd = getPelFd(pelId);
Ben Tyner1b1915e2020-10-23 15:13:38 -0500456
Ben Tynerf5210bb2021-01-05 12:58:10 -0600457 // if PEL found, read into buffer
458 if (-1 != pelFd)
459 {
460 auto pelSize = lseek(pelFd, 0, SEEK_END);
461 lseek(pelFd, 0, SEEK_SET);
Ben Tyner1b1915e2020-10-23 15:13:38 -0500462
Ben Tynerf5210bb2021-01-05 12:58:10 -0600463 // read information PEL into buffer
464 std::vector<uint8_t> buffer(pelSize);
465 read(pelFd, buffer.data(), buffer.size());
466 close(pelFd);
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500467
Ben Tynerf5210bb2021-01-05 12:58:10 -0600468 // create PEL from buffer
469 createPelCustom(buffer, i_additional);
470 }
471 }
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500472 }
473}
474
Ben Tynerf5210bb2021-01-05 12:58:10 -0600475/**
476 * Commit special attention TI event to log
477 *
478 * Create a event log with provided additional information and standard
479 * FFDC data plus TI FFDC data
480 *
481 * @param i_additional - Additional log data
482 * @param i_ti_InfoData - TI FFDC data
483 */
484void eventTerminate(std::map<std::string, std::string> i_additionalData,
485 char* i_tiInfoData)
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500486{
Ben Tynerb6401ed2021-01-11 09:08:07 -0600487 uint32_t tiInfoSize = 56; // assume not hypervisor TI
488
489 uint8_t subsystem = std::stoi(i_additionalData["Subsystem"]);
490
491 // If hypervisor
492 if (static_cast<uint8_t>(pel::SubsystemID::hypervisor) == subsystem)
493 {
494 tiInfoSize = 1024; // assume hypervisor max
495
496 // hypervisor may just want some of the data
497 if (0 == (*(i_tiInfoData + 0x09) & 0x01))
498 {
499 uint32_t* additionalLength = (uint32_t*)(i_tiInfoData + 0x50);
500 uint32_t tiAdditional = be32toh(*additionalLength);
501
502 tiInfoSize = std::min(tiInfoSize, (84 + tiAdditional));
503 }
504 }
505
506 std::string traceMsg = "TI info size = " + std::to_string(tiInfoSize);
507 trace<level::INFO>(traceMsg.c_str());
508
Ben Tynerf5210bb2021-01-05 12:58:10 -0600509 event(EventType::Terminate, i_additionalData,
Ben Tynerb6401ed2021-01-11 09:08:07 -0600510 createFFDCFiles(i_tiInfoData, tiInfoSize));
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500511}
512
Ben Tynerf5210bb2021-01-05 12:58:10 -0600513/** @brief Commit SBE vital event to log */
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500514void eventVital()
515{
Ben Tynerf5210bb2021-01-05 12:58:10 -0600516 // Additional data for log
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500517 std::map<std::string, std::string> additionalData;
518
Ben Tynerf5210bb2021-01-05 12:58:10 -0600519 // Create log event with additional data and FFDC data
520 event(EventType::Vital, additionalData, createFFDCFiles(nullptr, 0));
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500521}
522
Ben Tynerf5210bb2021-01-05 12:58:10 -0600523/**
Ben Tynerf5210bb2021-01-05 12:58:10 -0600524 * Commit attention handler failure event to log
525 *
526 * Create an event log containing the specified error code.
527 *
528 * @param i_error - Error code
529 */
530void eventAttentionFail(int i_error)
531{
532 // Additional data for log
533 std::map<std::string, std::string> additionalData;
534 additionalData["ERROR_CODE"] = std::to_string(i_error);
535
536 // Create log event with additional data and FFDC data
537 event(EventType::AttentionFail, additionalData,
538 createFFDCFiles(nullptr, 0));
539}
540
541/**
542 * Parse systemd journal message field
543 *
544 * Parse the journal looking for the specified field and return the journal
545 * data for that field.
546 *
547 * @param journal - The journal to parse
548 * @param field - Field containing the data to retrieve
549 * @return Data for the speciefied field
550 */
Ben Tyner1b1915e2020-10-23 15:13:38 -0500551std::string sdjGetFieldValue(sd_journal* journal, const char* field)
552{
553 const char* data{nullptr};
554 size_t length{0};
Ben Tyner1b1915e2020-10-23 15:13:38 -0500555
556 // get field value
557 if (0 == sd_journal_get_data(journal, field, (const void**)&data, &length))
558 {
Zane Shelley9fb657f2021-01-12 15:30:58 -0600559 size_t prefix{0};
560
Ben Tyner1b1915e2020-10-23 15:13:38 -0500561 // The data returned by sd_journal_get_data will be prefixed with the
562 // field name and "="
563 const void* eq = memchr(data, '=', length);
564 if (nullptr != eq)
565 {
566 // get just data following the "="
567 prefix = (const char*)eq - data + 1;
568 }
569 else
570 {
571 // all the data (should not happen)
572 prefix = 0;
573 std::string value{}; // empty string
574 }
575
576 return std::string{data + prefix, length - prefix};
577 }
578 else
579 {
580 return std::string{}; // empty string
581 }
582}
583
Ben Tynerf5210bb2021-01-05 12:58:10 -0600584/**
585 * Gather messages from the journal
586 *
587 * Fetch journal entry data for all entries with the specified field equal to
588 * the specified value.
589 *
590 * @param field - Field to search on
591 * @param fieldValue - Value to search for
592 * @param max - Maximum number of messages fetch
593 * @return Vector of journal entry data
594 */
Ben Tyner1b1915e2020-10-23 15:13:38 -0500595std::vector<std::string> sdjGetMessages(const std::string& field,
596 const std::string& fieldValue,
597 unsigned int max)
598{
599 sd_journal* journal;
600 std::vector<std::string> messages;
601
602 if (0 == sd_journal_open(&journal, SD_JOURNAL_LOCAL_ONLY))
603 {
604 SD_JOURNAL_FOREACH_BACKWARDS(journal)
605 {
606 // Get input field
607 std::string value = sdjGetFieldValue(journal, field.c_str());
608
609 // Compare field value and read data
610 if (value == fieldValue)
611 {
612 // Get SYSLOG_IDENTIFIER field (process that logged message)
613 std::string syslog =
614 sdjGetFieldValue(journal, "SYSLOG_IDENTIFIER");
615
616 // Get _PID field
617 std::string pid = sdjGetFieldValue(journal, "_PID");
618
619 // Get MESSAGE field
620 std::string message = sdjGetFieldValue(journal, "MESSAGE");
621
622 // Get timestamp
623 uint64_t usec{0};
624 if (0 == sd_journal_get_realtime_usec(journal, &usec))
625 {
626
627 // Convert realtime microseconds to date format
628 char dateBuffer[80];
629 std::string date;
630 std::time_t timeInSecs = usec / 1000000;
631 strftime(dateBuffer, sizeof(dateBuffer), "%b %d %H:%M:%S",
632 std::localtime(&timeInSecs));
633 date = dateBuffer;
634
635 // Store value to messages
636 value = date + " " + syslog + "[" + pid + "]: " + message;
637 messages.insert(messages.begin(), value);
638 }
639 }
640
641 // limit maximum number of messages
642 if (messages.size() >= max)
643 {
644 break;
645 }
646 }
647
648 sd_journal_close(journal); // close journal when done
649 }
650
651 return messages;
652}
653
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500654} // namespace attn