1 #include <fstream> 2 #include <iostream> 3 #include <chrono> 4 #include <cstdio> 5 #include <set> 6 #include <string> 7 #include <vector> 8 #include <sdbusplus/vtable.hpp> 9 #include <systemd/sd-bus.h> 10 #include <systemd/sd-journal.h> 11 #include "elog-lookup.cpp" 12 #include <phosphor-logging/elog-errors-HostEvent.hpp> 13 #include "config.h" 14 #include "elog_entry.hpp" 15 #include <phosphor-logging/log.hpp> 16 #include "log_manager.hpp" 17 #include "elog_meta.hpp" 18 19 using namespace phosphor::logging; 20 extern const std::map<metadata::Metadata, 21 std::function<metadata::associations::Type>> meta; 22 23 namespace phosphor 24 { 25 namespace logging 26 { 27 28 void Manager::commit(uint64_t transactionId, std::string errMsg) 29 { 30 constexpr const auto transactionIdVar = "TRANSACTION_ID"; 31 // Length of 'TRANSACTION_ID' string. 32 constexpr const auto transactionIdVarSize = strlen(transactionIdVar); 33 // Length of 'TRANSACTION_ID=' string. 34 constexpr const auto transactionIdVarOffset = transactionIdVarSize + 1; 35 36 sd_journal *j = nullptr; 37 int rc = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); 38 if (rc < 0) 39 { 40 logging::log<logging::level::ERR>("Failed to open journal", 41 logging::entry("DESCRIPTION=%s", strerror(-rc))); 42 return; 43 } 44 45 std::string transactionIdStr = std::to_string(transactionId); 46 std::set<std::string> metalist(g_errMetaMap[errMsg].begin(), 47 g_errMetaMap[errMsg].end()); 48 const auto& metalistHostEvent = g_errMetaMapHostEvent[errMsg]; 49 std::vector<std::string> additionalData; 50 51 // TODO Remove once host event error header file is auto-generated. 52 // Also make metalist a const variable. 53 // Tracking with issue openbmc/phosphor-logging#4 54 for (auto& metaVarStrHostEvent : metalistHostEvent) 55 { 56 metalist.insert(metaVarStrHostEvent); 57 } 58 59 // Read the journal from the end to get the most recent entry first. 60 // The result from the sd_journal_get_data() is of the form VARIABLE=value. 61 SD_JOURNAL_FOREACH_BACKWARDS(j) 62 { 63 const char *data = nullptr; 64 size_t length = 0; 65 66 // Look for the transaction id metadata variable 67 rc = sd_journal_get_data(j, transactionIdVar, (const void **)&data, 68 &length); 69 if (rc < 0) 70 { 71 // This journal entry does not have the TRANSACTION_ID 72 // metadata variable. 73 continue; 74 } 75 76 // journald does not guarantee that sd_journal_get_data() returns NULL 77 // terminated strings, so need to specify the size to use to compare, 78 // use the returned length instead of anything that relies on NULL 79 // terminators like strlen(). 80 // The data variable is in the form of 'TRANSACTION_ID=1234'. Remove 81 // the TRANSACTION_ID characters plus the (=) sign to do the comparison. 82 // 'data + transactionIdVarOffset' will be in the form of '1234'. 83 // 'length - transactionIdVarOffset' will be the length of '1234'. 84 if ((length <= (transactionIdVarOffset)) || 85 (transactionIdStr.compare(0, 86 transactionIdStr.size(), 87 data + transactionIdVarOffset, 88 length - transactionIdVarOffset) != 0)) 89 { 90 // The value of the TRANSACTION_ID metadata is not the requested 91 // transaction id number. 92 continue; 93 } 94 95 // Search for all metadata variables in the current journal entry. 96 for (auto i = metalist.cbegin(); i != metalist.cend();) 97 { 98 rc = sd_journal_get_data(j, (*i).c_str(), 99 (const void **)&data, &length); 100 if (rc < 0) 101 { 102 // Metadata variable not found, check next metadata variable. 103 i++; 104 continue; 105 } 106 107 // Metadata variable found, save it and remove it from the set. 108 additionalData.emplace_back(data, length); 109 i = metalist.erase(i); 110 } 111 if (metalist.empty()) 112 { 113 // All metadata variables found, break out of journal loop. 114 break; 115 } 116 } 117 if (!metalist.empty()) 118 { 119 // Not all the metadata variables were found in the journal. 120 for (auto& metaVarStr : metalist) 121 { 122 logging::log<logging::level::INFO>("Failed to find metadata", 123 logging::entry("META_FIELD=%s", metaVarStr.c_str())); 124 } 125 } 126 127 sd_journal_close(j); 128 129 // Create error Entry dbus object 130 entryId++; 131 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>( 132 std::chrono::system_clock::now().time_since_epoch()).count(); 133 auto objPath = std::string(OBJ_ENTRY) + '/' + 134 std::to_string(entryId); 135 136 AssociationList objects {}; 137 processMetadata(errMsg, additionalData, objects); 138 139 entries.insert(std::make_pair(entryId, std::make_unique<Entry>( 140 busLog, 141 objPath, 142 entryId, 143 ms, // Milliseconds since 1970 144 (Entry::Level)g_errLevelMap[errMsg], 145 std::move(errMsg), 146 std::move(additionalData), 147 std::move(objects)))); 148 return; 149 } 150 151 void Manager::processMetadata(const std::string& errorName, 152 const std::vector<std::string>& additionalData, 153 AssociationList& objects) const 154 { 155 // additionalData is a list of "metadata=value" 156 constexpr auto separator = '='; 157 for(const auto& entry: additionalData) 158 { 159 auto found = entry.find(separator); 160 if(std::string::npos != found) 161 { 162 auto metadata = entry.substr(0, found); 163 auto iter = meta.find(metadata); 164 if(meta.end() != iter) 165 { 166 (iter->second)(metadata, additionalData, objects); 167 } 168 } 169 } 170 } 171 172 } // namespace logging 173 } // namepsace phosphor 174