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 "config.h" 12 #include "elog_entry.hpp" 13 #include <phosphor-logging/log.hpp> 14 #include "log_manager.hpp" 15 #include "elog_meta.hpp" 16 #include "elog_serialize.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 namespace internal 27 { 28 void Manager::commit(uint64_t transactionId, std::string errMsg) 29 { 30 auto reqLevel = level::ERR; // Default to ERR 31 auto levelmap = g_errLevelMap.find(errMsg); 32 33 if (levelmap != g_errLevelMap.end()) 34 { 35 reqLevel = levelmap->second; 36 } 37 38 if (static_cast<Entry::Level>(reqLevel) < Entry::sevLowerLimit) 39 { 40 if (realErrors.size() >= ERROR_CAP) 41 { 42 erase(realErrors.front()); 43 } 44 } 45 else 46 { 47 if (infoErrors.size() >= ERROR_INFO_CAP) 48 { 49 erase(infoErrors.front()); 50 } 51 } 52 constexpr const auto transactionIdVar = "TRANSACTION_ID"; 53 // Length of 'TRANSACTION_ID' string. 54 constexpr const auto transactionIdVarSize = strlen(transactionIdVar); 55 // Length of 'TRANSACTION_ID=' string. 56 constexpr const auto transactionIdVarOffset = transactionIdVarSize + 1; 57 58 // Flush all the pending log messages into the journal via Synchronize 59 constexpr auto JOURNAL_BUSNAME = "org.freedesktop.journal1"; 60 constexpr auto JOURNAL_PATH = "/org/freedesktop/journal1"; 61 constexpr auto JOURNAL_INTERFACE = "org.freedesktop.journal1"; 62 auto bus = sdbusplus::bus::new_default(); 63 auto method = bus.new_method_call(JOURNAL_BUSNAME, JOURNAL_PATH, 64 JOURNAL_INTERFACE, "Synchronize"); 65 bus.call_noreply(method); 66 67 sd_journal *j = nullptr; 68 int rc = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); 69 if (rc < 0) 70 { 71 logging::log<logging::level::ERR>("Failed to open journal", 72 logging::entry("DESCRIPTION=%s", strerror(-rc))); 73 return; 74 } 75 76 std::string transactionIdStr = std::to_string(transactionId); 77 std::set<std::string> metalist; 78 auto metamap = g_errMetaMap.find(errMsg); 79 if (metamap != g_errMetaMap.end()) 80 { 81 metalist.insert(metamap->second.begin(), metamap->second.end()); 82 } 83 84 //Add _PID field information in AdditionalData. 85 metalist.insert("_PID"); 86 87 std::vector<std::string> additionalData; 88 89 // Read the journal from the end to get the most recent entry first. 90 // The result from the sd_journal_get_data() is of the form VARIABLE=value. 91 SD_JOURNAL_FOREACH_BACKWARDS(j) 92 { 93 const char *data = nullptr; 94 size_t length = 0; 95 96 // Look for the transaction id metadata variable 97 rc = sd_journal_get_data(j, transactionIdVar, (const void **)&data, 98 &length); 99 if (rc < 0) 100 { 101 // This journal entry does not have the TRANSACTION_ID 102 // metadata variable. 103 continue; 104 } 105 106 // journald does not guarantee that sd_journal_get_data() returns NULL 107 // terminated strings, so need to specify the size to use to compare, 108 // use the returned length instead of anything that relies on NULL 109 // terminators like strlen(). 110 // The data variable is in the form of 'TRANSACTION_ID=1234'. Remove 111 // the TRANSACTION_ID characters plus the (=) sign to do the comparison. 112 // 'data + transactionIdVarOffset' will be in the form of '1234'. 113 // 'length - transactionIdVarOffset' will be the length of '1234'. 114 if ((length <= (transactionIdVarOffset)) || 115 (transactionIdStr.compare(0, 116 transactionIdStr.size(), 117 data + transactionIdVarOffset, 118 length - transactionIdVarOffset) != 0)) 119 { 120 // The value of the TRANSACTION_ID metadata is not the requested 121 // transaction id number. 122 continue; 123 } 124 125 // Search for all metadata variables in the current journal entry. 126 for (auto i = metalist.cbegin(); i != metalist.cend();) 127 { 128 rc = sd_journal_get_data(j, (*i).c_str(), 129 (const void **)&data, &length); 130 if (rc < 0) 131 { 132 // Metadata variable not found, check next metadata variable. 133 i++; 134 continue; 135 } 136 137 // Metadata variable found, save it and remove it from the set. 138 additionalData.emplace_back(data, length); 139 i = metalist.erase(i); 140 } 141 if (metalist.empty()) 142 { 143 // All metadata variables found, break out of journal loop. 144 break; 145 } 146 } 147 if (!metalist.empty()) 148 { 149 // Not all the metadata variables were found in the journal. 150 for (auto& metaVarStr : metalist) 151 { 152 logging::log<logging::level::INFO>("Failed to find metadata", 153 logging::entry("META_FIELD=%s", metaVarStr.c_str())); 154 } 155 } 156 157 sd_journal_close(j); 158 159 // Create error Entry dbus object 160 entryId++; 161 if (static_cast<Entry::Level>(reqLevel) >= Entry::sevLowerLimit) 162 { 163 infoErrors.push_back(entryId); 164 } 165 else 166 { 167 realErrors.push_back(entryId); 168 } 169 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>( 170 std::chrono::system_clock::now().time_since_epoch()).count(); 171 auto objPath = std::string(OBJ_ENTRY) + '/' + 172 std::to_string(entryId); 173 174 AssociationList objects {}; 175 processMetadata(errMsg, additionalData, objects); 176 177 auto e = std::make_unique<Entry>( 178 busLog, 179 objPath, 180 entryId, 181 ms, // Milliseconds since 1970 182 static_cast<Entry::Level>(reqLevel), 183 std::move(errMsg), 184 std::move(additionalData), 185 std::move(objects), 186 *this); 187 serialize(*e); 188 entries.insert(std::make_pair(entryId, std::move(e))); 189 } 190 191 void Manager::processMetadata(const std::string& errorName, 192 const std::vector<std::string>& additionalData, 193 AssociationList& objects) const 194 { 195 // additionalData is a list of "metadata=value" 196 constexpr auto separator = '='; 197 for(const auto& entry: additionalData) 198 { 199 auto found = entry.find(separator); 200 if(std::string::npos != found) 201 { 202 auto metadata = entry.substr(0, found); 203 auto iter = meta.find(metadata); 204 if(meta.end() != iter) 205 { 206 (iter->second)(metadata, additionalData, objects); 207 } 208 } 209 } 210 } 211 212 void Manager::erase(uint32_t entryId) 213 { 214 auto entry = entries.find(entryId); 215 if(entries.end() != entry) 216 { 217 // Delete the persistent representation of this error. 218 fs::path errorPath(ERRLOG_PERSIST_PATH); 219 errorPath /= std::to_string(entryId); 220 fs::remove(errorPath); 221 222 auto removeId = [](std::list<uint32_t>& ids , uint32_t id) 223 { 224 auto it = std::find(ids.begin(), ids.end(), id); 225 if (it != ids.end()) 226 { 227 ids.erase(it); 228 } 229 }; 230 if (entry->second->severity() >= Entry::sevLowerLimit) 231 { 232 removeId(infoErrors, entryId); 233 } 234 else 235 { 236 removeId(realErrors, entryId); 237 } 238 entries.erase(entry); 239 } 240 else 241 { 242 logging::log<level::ERR>("Invalid entry ID to delete", 243 logging::entry("ID=%d", entryId)); 244 } 245 } 246 247 void Manager::restore() 248 { 249 auto sanity = [](const auto& id, const auto& restoredId) 250 { 251 return id == restoredId; 252 }; 253 std::vector<uint32_t> errorIds; 254 255 fs::path dir(ERRLOG_PERSIST_PATH); 256 if (!fs::exists(dir) || fs::is_empty(dir)) 257 { 258 return; 259 } 260 261 for(auto& file: fs::directory_iterator(dir)) 262 { 263 auto id = file.path().filename().c_str(); 264 auto idNum = std::stol(id); 265 auto e = std::make_unique<Entry>( 266 busLog, 267 std::string(OBJ_ENTRY) + '/' + id, 268 idNum, 269 *this); 270 if (deserialize(file.path(), *e)) 271 { 272 //validate the restored error entry id 273 if (sanity(static_cast<uint32_t>(idNum), e->id())) 274 { 275 e->emit_object_added(); 276 if (e->severity() >= Entry::sevLowerLimit) 277 { 278 infoErrors.push_back(idNum); 279 } 280 else 281 { 282 realErrors.push_back(idNum); 283 } 284 285 entries.insert(std::make_pair(idNum, std::move(e))); 286 errorIds.push_back(idNum); 287 } 288 else 289 { 290 logging::log<logging::level::ERR>( 291 "Failed in sanity check while restoring error entry. " 292 "Ignoring error entry", 293 logging::entry("ID_NUM=%d", idNum), 294 logging::entry("ENTRY_ID=%d", e->id())); 295 } 296 } 297 } 298 299 if (!errorIds.empty()) 300 { 301 entryId = *(std::max_element(errorIds.begin(), errorIds.end())); 302 } 303 } 304 305 } // namespace internal 306 } // namespace logging 307 } // namepsace phosphor 308