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