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