1*faf33367SBen Tyner #include <util/ffdc_file.hpp> 2*faf33367SBen Tyner #include <util/trace.hpp> 3*faf33367SBen Tyner 4*faf33367SBen Tyner #include <string> 5*faf33367SBen Tyner #include <vector> 6*faf33367SBen Tyner 7*faf33367SBen Tyner namespace util 8*faf33367SBen Tyner { 9*faf33367SBen Tyner 10*faf33367SBen Tyner /** 11*faf33367SBen Tyner * Parse systemd journal message field 12*faf33367SBen Tyner * 13*faf33367SBen Tyner * Parse the journal looking for the specified field and return the journal 14*faf33367SBen Tyner * data for that field. 15*faf33367SBen Tyner * 16*faf33367SBen Tyner * @param journal - The journal to parse 17*faf33367SBen Tyner * @param field - Field containing the data to retrieve 18*faf33367SBen Tyner * @return Data for the speciefied field 19*faf33367SBen Tyner */ 20*faf33367SBen Tyner std::string sdjGetFieldValue(sd_journal* journal, const char* field) 21*faf33367SBen Tyner { 22*faf33367SBen Tyner const char* data{nullptr}; 23*faf33367SBen Tyner size_t length{0}; 24*faf33367SBen Tyner 25*faf33367SBen Tyner // get field value 26*faf33367SBen Tyner if (0 == sd_journal_get_data(journal, field, (const void**)&data, &length)) 27*faf33367SBen Tyner { 28*faf33367SBen Tyner size_t prefix{0}; 29*faf33367SBen Tyner 30*faf33367SBen Tyner // The data returned by sd_journal_get_data will be prefixed with the 31*faf33367SBen Tyner // field name and "=" 32*faf33367SBen Tyner const void* eq = memchr(data, '=', length); 33*faf33367SBen Tyner if (nullptr != eq) 34*faf33367SBen Tyner { 35*faf33367SBen Tyner // get just data following the "=" 36*faf33367SBen Tyner prefix = (const char*)eq - data + 1; 37*faf33367SBen Tyner } 38*faf33367SBen Tyner else 39*faf33367SBen Tyner { 40*faf33367SBen Tyner // all the data (should not happen) 41*faf33367SBen Tyner prefix = 0; 42*faf33367SBen Tyner std::string value{}; // empty string 43*faf33367SBen Tyner } 44*faf33367SBen Tyner 45*faf33367SBen Tyner return std::string{data + prefix, length - prefix}; 46*faf33367SBen Tyner } 47*faf33367SBen Tyner else 48*faf33367SBen Tyner { 49*faf33367SBen Tyner return std::string{}; // empty string 50*faf33367SBen Tyner } 51*faf33367SBen Tyner } 52*faf33367SBen Tyner 53*faf33367SBen Tyner /** 54*faf33367SBen Tyner * Gather messages from the journal 55*faf33367SBen Tyner * 56*faf33367SBen Tyner * Fetch journal entry data for all entries with the specified field equal to 57*faf33367SBen Tyner * the specified value. 58*faf33367SBen Tyner * 59*faf33367SBen Tyner * @param field - Field to search on 60*faf33367SBen Tyner * @param fieldValue - Value to search for 61*faf33367SBen Tyner * @param max - Maximum number of messages fetch 62*faf33367SBen Tyner * @return Vector of journal entry data 63*faf33367SBen Tyner */ 64*faf33367SBen Tyner std::vector<std::string> sdjGetMessages(const std::string& field, 65*faf33367SBen Tyner const std::string& fieldValue, 66*faf33367SBen Tyner unsigned int max) 67*faf33367SBen Tyner { 68*faf33367SBen Tyner sd_journal* journal; 69*faf33367SBen Tyner std::vector<std::string> messages; 70*faf33367SBen Tyner 71*faf33367SBen Tyner if (0 == sd_journal_open(&journal, SD_JOURNAL_LOCAL_ONLY)) 72*faf33367SBen Tyner { 73*faf33367SBen Tyner SD_JOURNAL_FOREACH_BACKWARDS(journal) 74*faf33367SBen Tyner { 75*faf33367SBen Tyner // Get input field 76*faf33367SBen Tyner std::string value = sdjGetFieldValue(journal, field.c_str()); 77*faf33367SBen Tyner 78*faf33367SBen Tyner // Compare field value and read data 79*faf33367SBen Tyner if (value == fieldValue) 80*faf33367SBen Tyner { 81*faf33367SBen Tyner // Get SYSLOG_IDENTIFIER field (process that logged message) 82*faf33367SBen Tyner std::string syslog = 83*faf33367SBen Tyner sdjGetFieldValue(journal, "SYSLOG_IDENTIFIER"); 84*faf33367SBen Tyner 85*faf33367SBen Tyner // Get _PID field 86*faf33367SBen Tyner std::string pid = sdjGetFieldValue(journal, "_PID"); 87*faf33367SBen Tyner 88*faf33367SBen Tyner // Get MESSAGE field 89*faf33367SBen Tyner std::string message = sdjGetFieldValue(journal, "MESSAGE"); 90*faf33367SBen Tyner 91*faf33367SBen Tyner // Get timestamp 92*faf33367SBen Tyner uint64_t usec{0}; 93*faf33367SBen Tyner if (0 == sd_journal_get_realtime_usec(journal, &usec)) 94*faf33367SBen Tyner { 95*faf33367SBen Tyner 96*faf33367SBen Tyner // Convert realtime microseconds to date format 97*faf33367SBen Tyner char dateBuffer[80]; 98*faf33367SBen Tyner std::string date; 99*faf33367SBen Tyner std::time_t timeInSecs = usec / 1000000; 100*faf33367SBen Tyner strftime(dateBuffer, sizeof(dateBuffer), "%b %d %H:%M:%S", 101*faf33367SBen Tyner std::localtime(&timeInSecs)); 102*faf33367SBen Tyner date = dateBuffer; 103*faf33367SBen Tyner 104*faf33367SBen Tyner // Store value to messages 105*faf33367SBen Tyner value = date + " " + syslog + "[" + pid + "]: " + message; 106*faf33367SBen Tyner messages.insert(messages.begin(), value); 107*faf33367SBen Tyner } 108*faf33367SBen Tyner } 109*faf33367SBen Tyner 110*faf33367SBen Tyner // limit maximum number of messages 111*faf33367SBen Tyner if (messages.size() >= max) 112*faf33367SBen Tyner { 113*faf33367SBen Tyner break; 114*faf33367SBen Tyner } 115*faf33367SBen Tyner } 116*faf33367SBen Tyner 117*faf33367SBen Tyner sd_journal_close(journal); // close journal when done 118*faf33367SBen Tyner } 119*faf33367SBen Tyner 120*faf33367SBen Tyner return messages; 121*faf33367SBen Tyner } 122*faf33367SBen Tyner /** 123*faf33367SBen Tyner * @brief Create an FFDCFile object containing the specified lines of text data 124*faf33367SBen Tyner * 125*faf33367SBen Tyner * Throws an exception if an error occurs. 126*faf33367SBen Tyner * 127*faf33367SBen Tyner * @param lines - lines of text data to write to file 128*faf33367SBen Tyner * @return FFDCFile object 129*faf33367SBen Tyner */ 130*faf33367SBen Tyner FFDCFile createFFDCTraceFile(const std::vector<std::string>& lines) 131*faf33367SBen Tyner { 132*faf33367SBen Tyner // Create FFDC file of type Text 133*faf33367SBen Tyner FFDCFile file{FFDCFormat::Text}; 134*faf33367SBen Tyner int fd = file.getFileDescriptor(); 135*faf33367SBen Tyner 136*faf33367SBen Tyner // Write FFDC lines to file 137*faf33367SBen Tyner std::string buffer; 138*faf33367SBen Tyner for (const std::string& line : lines) 139*faf33367SBen Tyner { 140*faf33367SBen Tyner // Copy line to buffer. Add newline if necessary. 141*faf33367SBen Tyner buffer = line; 142*faf33367SBen Tyner if (line.empty() || (line.back() != '\n')) 143*faf33367SBen Tyner { 144*faf33367SBen Tyner buffer += '\n'; 145*faf33367SBen Tyner } 146*faf33367SBen Tyner 147*faf33367SBen Tyner // write buffer to file 148*faf33367SBen Tyner size_t numBytes = write(fd, buffer.c_str(), buffer.size()); 149*faf33367SBen Tyner if (buffer.size() != numBytes) 150*faf33367SBen Tyner { 151*faf33367SBen Tyner trace::err("%s only %u of %u bytes written", file.getPath().c_str(), 152*faf33367SBen Tyner numBytes, buffer.size()); 153*faf33367SBen Tyner } 154*faf33367SBen Tyner } 155*faf33367SBen Tyner 156*faf33367SBen Tyner // Seek to beginning of file so error logging system can read data 157*faf33367SBen Tyner lseek(fd, 0, SEEK_SET); 158*faf33367SBen Tyner 159*faf33367SBen Tyner return file; 160*faf33367SBen Tyner } 161*faf33367SBen Tyner 162*faf33367SBen Tyner /** 163*faf33367SBen Tyner * Create FDDC files from journal messages of relevant executables 164*faf33367SBen Tyner * 165*faf33367SBen Tyner * Parse the system journal looking for log entries created by the executables 166*faf33367SBen Tyner * of interest for logging. For each of these entries create a ffdc trace file 167*faf33367SBen Tyner * that will be used to create ffdc log entries. These files will be pushed 168*faf33367SBen Tyner * onto the stack of ffdc files. 169*faf33367SBen Tyner * 170*faf33367SBen Tyner * @param i_files - vector of ffdc files that will become log entries 171*faf33367SBen Tyner */ 172*faf33367SBen Tyner void createFFDCTraceFiles(std::vector<FFDCFile>& i_files) 173*faf33367SBen Tyner { 174*faf33367SBen Tyner // Executables of interest 175*faf33367SBen Tyner std::vector<std::string> executables{"openpower-hw-diags"}; 176*faf33367SBen Tyner 177*faf33367SBen Tyner for (const std::string& executable : executables) 178*faf33367SBen Tyner { 179*faf33367SBen Tyner try 180*faf33367SBen Tyner { 181*faf33367SBen Tyner // get journal messages 182*faf33367SBen Tyner std::vector<std::string> messages = 183*faf33367SBen Tyner sdjGetMessages("SYSLOG_IDENTIFIER", executable, 30); 184*faf33367SBen Tyner 185*faf33367SBen Tyner // Create FFDC file containing the journal messages 186*faf33367SBen Tyner if (!messages.empty()) 187*faf33367SBen Tyner { 188*faf33367SBen Tyner i_files.emplace_back(createFFDCTraceFile(messages)); 189*faf33367SBen Tyner } 190*faf33367SBen Tyner } 191*faf33367SBen Tyner catch (const std::exception& e) 192*faf33367SBen Tyner { 193*faf33367SBen Tyner trace::inf("createFFDCTraceFiles exception"); 194*faf33367SBen Tyner trace::inf(e.what()); 195*faf33367SBen Tyner } 196*faf33367SBen Tyner } 197*faf33367SBen Tyner } 198*faf33367SBen Tyner 199*faf33367SBen Tyner } // namespace util 200