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