#include #include #include #include namespace util { /** * Parse systemd journal message field * * Parse the journal looking for the specified field and return the journal * data for that field. * * @param journal - The journal to parse * @param field - Field containing the data to retrieve * @return Data for the speciefied field */ std::string sdjGetFieldValue(sd_journal* journal, const char* field) { const char* data{nullptr}; size_t length{0}; // get field value if (0 == sd_journal_get_data(journal, field, (const void**)&data, &length)) { size_t prefix{0}; // The data returned by sd_journal_get_data will be prefixed with the // field name and "=" const void* eq = memchr(data, '=', length); if (nullptr != eq) { // get just data following the "=" prefix = (const char*)eq - data + 1; } else { // all the data (should not happen) prefix = 0; std::string value{}; // empty string } return std::string{data + prefix, length - prefix}; } else { return std::string{}; // empty string } } /** * Gather messages from the journal * * Fetch journal entry data for all entries with the specified field equal to * the specified value. * * @param field - Field to search on * @param fieldValue - Value to search for * @param max - Maximum number of messages fetch * @return Vector of journal entry data */ std::vector sdjGetMessages( const std::string& field, const std::string& fieldValue, unsigned int max) { sd_journal* journal; std::vector messages; if (0 == sd_journal_open(&journal, SD_JOURNAL_LOCAL_ONLY)) { SD_JOURNAL_FOREACH_BACKWARDS(journal) { // Get input field std::string value = sdjGetFieldValue(journal, field.c_str()); // Compare field value and read data if (value == fieldValue) { // Get SYSLOG_IDENTIFIER field (process that logged message) std::string syslog = sdjGetFieldValue(journal, "SYSLOG_IDENTIFIER"); // Get _PID field std::string pid = sdjGetFieldValue(journal, "_PID"); // Get MESSAGE field std::string message = sdjGetFieldValue(journal, "MESSAGE"); // Get timestamp uint64_t usec{0}; if (0 == sd_journal_get_realtime_usec(journal, &usec)) { // Convert realtime microseconds to date format char dateBuffer[80]; std::string date; std::time_t timeInSecs = usec / 1000000; strftime(dateBuffer, sizeof(dateBuffer), "%b %d %H:%M:%S", std::localtime(&timeInSecs)); date = dateBuffer; // Store value to messages value = date + " " + syslog + "[" + pid + "]: " + message; messages.insert(messages.begin(), value); } } // limit maximum number of messages if (messages.size() >= max) { break; } } sd_journal_close(journal); // close journal when done } return messages; } /** * @brief Create an FFDCFile object containing the specified lines of text data * * Throws an exception if an error occurs. * * @param lines - lines of text data to write to file * @return FFDCFile object */ FFDCFile createFFDCTraceFile(const std::vector& lines) { // Create FFDC file of type Text FFDCFile file{FFDCFormat::Text}; int fd = file.getFileDescriptor(); // Write FFDC lines to file std::string buffer; for (const std::string& line : lines) { // Copy line to buffer. Add newline if necessary. buffer = line; if (line.empty() || (line.back() != '\n')) { buffer += '\n'; } // write buffer to file size_t numBytes = write(fd, buffer.c_str(), buffer.size()); if (buffer.size() != numBytes) { trace::err("%s only %u of %u bytes written", file.getPath().c_str(), numBytes, buffer.size()); } } // Seek to beginning of file so error logging system can read data lseek(fd, 0, SEEK_SET); return file; } /** * Create FDDC files from journal messages of relevant executables * * Parse the system journal looking for log entries created by the executables * of interest for logging. For each of these entries create a ffdc trace file * that will be used to create ffdc log entries. These files will be pushed * onto the stack of ffdc files. * * @param i_files - vector of ffdc files that will become log entries */ void createFFDCTraceFiles(std::vector& i_files) { // Executables of interest std::vector executables{"openpower-hw-diags"}; for (const std::string& executable : executables) { try { // get journal messages std::vector messages = sdjGetMessages("SYSLOG_IDENTIFIER", executable, 30); // Create FFDC file containing the journal messages if (!messages.empty()) { i_files.emplace_back(createFFDCTraceFile(messages)); } } catch (const std::exception& e) { trace::inf("createFFDCTraceFiles exception"); trace::inf(e.what()); } } } } // namespace util