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