1 #include "config.h" 2 3 #include "elog_serialize.hpp" 4 5 #include <cereal/archives/binary.hpp> 6 #include <cereal/types/map.hpp> 7 #include <cereal/types/string.hpp> 8 #include <cereal/types/tuple.hpp> 9 #include <cereal/types/vector.hpp> 10 #include <phosphor-logging/lg2.hpp> 11 12 #include <fstream> 13 14 // Register class version 15 // From cereal documentation; 16 // "This macro should be placed at global scope" 17 CEREAL_CLASS_VERSION(phosphor::logging::Entry, CLASS_VERSION) 18 19 namespace phosphor 20 { 21 namespace logging 22 { 23 24 /** @brief Function required by Cereal to perform serialization. 25 * @tparam Archive - Cereal archive type (binary in our case). 26 * @param[in] a - reference to Cereal archive. 27 * @param[in] e - const reference to error entry. 28 * @param[in] version - Class version that enables handling 29 * a serialized data across code levels 30 */ 31 template <class Archive> 32 void save(Archive& a, const Entry& e, const std::uint32_t /*version*/) 33 { 34 a(e.id(), e.severity(), e.timestamp(), e.message(), 35 util::additional_data::combine(e.additionalData()), e.associations(), 36 e.resolved(), e.version(), e.updateTimestamp(), e.eventId(), 37 e.resolution()); 38 } 39 40 /** @brief Function required by Cereal to perform deserialization. 41 * @tparam Archive - Cereal archive type (binary in our case). 42 * @param[in] a - reference to Cereal archive. 43 * @param[in] e - reference to error entry. 44 * @param[in] version - Class version that enables handling 45 * a serialized data across code levels 46 */ 47 template <class Archive> 48 void load(Archive& a, Entry& e, const std::uint32_t version) 49 { 50 using namespace sdbusplus::server::xyz::openbmc_project::logging; 51 52 uint32_t id{}; 53 Entry::Level severity{}; 54 uint64_t timestamp{}; 55 std::string message{}; 56 std::map<std::string, std::string> additionalData{}; 57 bool resolved{}; 58 AssociationList associations{}; 59 std::string fwVersion{}; 60 uint64_t updateTimestamp{}; 61 std::string eventId{}; 62 std::string resolution{}; 63 64 if (version < std::stoul(FIRST_CEREAL_CLASS_VERSION_WITH_FWLEVEL)) 65 { 66 std::vector<std::string> additionalData_old{}; 67 a(id, severity, timestamp, message, additionalData_old, associations, 68 resolved); 69 updateTimestamp = timestamp; 70 additionalData = util::additional_data::parse(additionalData_old); 71 } 72 else if (version < std::stoul(FIRST_CEREAL_CLASS_VERSION_WITH_UPDATE_TS)) 73 { 74 std::vector<std::string> additionalData_old{}; 75 a(id, severity, timestamp, message, additionalData_old, associations, 76 resolved, fwVersion); 77 updateTimestamp = timestamp; 78 additionalData = util::additional_data::parse(additionalData_old); 79 } 80 else if (version < std::stoul(FIRST_CEREAL_CLASS_VERSION_WITH_EVENTID)) 81 { 82 std::vector<std::string> additionalData_old{}; 83 a(id, severity, timestamp, message, additionalData_old, associations, 84 resolved, fwVersion, updateTimestamp); 85 additionalData = util::additional_data::parse(additionalData_old); 86 } 87 else if (version < std::stoul(FIRST_CEREAL_CLASS_VERSION_WITH_RESOLUTION)) 88 { 89 std::vector<std::string> additionalData_old{}; 90 a(id, severity, timestamp, message, additionalData_old, associations, 91 resolved, fwVersion, updateTimestamp, eventId); 92 additionalData = util::additional_data::parse(additionalData_old); 93 } 94 else if (version < 95 std::stoul(FIRST_CEREAL_CLASS_VERSION_WITH_METADATA_DICT)) 96 { 97 std::vector<std::string> additionalData_old{}; 98 a(id, severity, timestamp, message, additionalData_old, associations, 99 resolved, fwVersion, updateTimestamp, eventId, resolution); 100 additionalData = util::additional_data::parse(additionalData_old); 101 } 102 else if (version == 103 std::stoul(FIRST_CEREAL_CLASS_VERSION_WITH_METADATA_DICT)) 104 { 105 a(id, severity, timestamp, message, additionalData, associations, 106 resolved, fwVersion, updateTimestamp, eventId, resolution); 107 } 108 else 109 { 110 // Go back to reading a vector for additionalData 111 std::vector<std::string> additionalData_old{}; 112 a(id, severity, timestamp, message, additionalData_old, associations, 113 resolved, fwVersion, updateTimestamp, eventId, resolution); 114 additionalData = util::additional_data::parse(additionalData_old); 115 } 116 117 e.id(id, true); 118 e.severity(severity, true); 119 e.timestamp(timestamp, true); 120 e.message(message, true); 121 e.additionalData(additionalData, true); 122 e.sdbusplus::server::xyz::openbmc_project::logging::Entry::resolved( 123 resolved, true); 124 e.associations(associations, true); 125 e.version(fwVersion, true); 126 e.purpose(sdbusplus::server::xyz::openbmc_project::software::Version:: 127 VersionPurpose::BMC, 128 true); 129 e.updateTimestamp(updateTimestamp, true); 130 e.eventId(eventId, true); 131 e.resolution(resolution, true); 132 } 133 134 fs::path getEntrySerializePath(uint32_t id, const fs::path& dir) 135 { 136 return dir / std::to_string(id); 137 } 138 139 fs::path serialize(const Entry& e, const fs::path& dir) 140 { 141 auto path = getEntrySerializePath(e.id(), dir); 142 std::ofstream os(path.c_str(), std::ios::binary); 143 cereal::BinaryOutputArchive oarchive(os); 144 oarchive(e); 145 return path; 146 } 147 148 bool deserialize(const fs::path& path, Entry& e) 149 { 150 try 151 { 152 if (fs::exists(path)) 153 { 154 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary); 155 cereal::BinaryInputArchive iarchive(is); 156 iarchive(e); 157 return true; 158 } 159 return false; 160 } 161 catch (const std::exception& ex) 162 { 163 lg2::error("Failed restoring {PATH}: {EXCEPTION}", "PATH", path, 164 "EXCEPTION", ex); 165 // Save it for later debug. Just write over any previous ones. 166 auto saveDir = paths::error().parent_path(); 167 fs::rename(path, saveDir / "corrupt_error"); 168 return false; 169 } 170 } 171 172 } // namespace logging 173 } // namespace phosphor 174