1 #include "config.h"
2 
3 #include "elog_serialize.hpp"
4 
5 #include <cereal/archives/binary.hpp>
6 #include <cereal/types/string.hpp>
7 #include <cereal/types/tuple.hpp>
8 #include <cereal/types/vector.hpp>
9 #include <fstream>
10 #include <phosphor-logging/log.hpp>
11 
12 // Register class version
13 // From cereal documentation;
14 // "This macro should be placed at global scope"
15 CEREAL_CLASS_VERSION(phosphor::logging::Entry, CLASS_VERSION)
16 
17 namespace phosphor
18 {
19 namespace logging
20 {
21 
22 /** @brief Function required by Cereal to perform serialization.
23  *  @tparam Archive - Cereal archive type (binary in our case).
24  *  @param[in] a       - reference to Cereal archive.
25  *  @param[in] e       - const reference to error entry.
26  *  @param[in] version - Class version that enables handling
27  *                       a serialized data across code levels
28  */
29 template <class Archive>
30 void save(Archive& a, const Entry& e, const std::uint32_t /*version*/)
31 {
32     a(e.id(), e.severity(), e.timestamp(), e.message(), e.additionalData(),
33       e.associations(), e.resolved(), e.version(), e.updateTimestamp(),
34       e.eventId(), e.resolution());
35 }
36 
37 /** @brief Function required by Cereal to perform deserialization.
38  *  @tparam Archive - Cereal archive type (binary in our case).
39  *  @param[in] a       - reference to Cereal archive.
40  *  @param[in] e       - reference to error entry.
41  *  @param[in] version - Class version that enables handling
42  *                       a serialized data across code levels
43  */
44 template <class Archive>
45 void load(Archive& a, Entry& e, const std::uint32_t version)
46 {
47     using namespace sdbusplus::xyz::openbmc_project::Logging::server;
48 
49     uint32_t id{};
50     Entry::Level severity{};
51     uint64_t timestamp{};
52     std::string message{};
53     std::vector<std::string> additionalData{};
54     bool resolved{};
55     AssociationList associations{};
56     std::string fwVersion{};
57     uint64_t updateTimestamp{};
58     std::string eventId{};
59     std::string resolution{};
60 
61     if (version < std::stoul(FIRST_CEREAL_CLASS_VERSION_WITH_FWLEVEL))
62     {
63         a(id, severity, timestamp, message, additionalData, associations,
64           resolved);
65         updateTimestamp = timestamp;
66     }
67     else if (version < std::stoul(FIRST_CEREAL_CLASS_VERSION_WITH_UPDATE_TS))
68     {
69         a(id, severity, timestamp, message, additionalData, associations,
70           resolved, fwVersion);
71         updateTimestamp = timestamp;
72     }
73     else if (version < std::stoul(FIRST_CEREAL_CLASS_VERSION_WITH_EVENTID))
74     {
75         a(id, severity, timestamp, message, additionalData, associations,
76           resolved, fwVersion, updateTimestamp);
77     }
78     else if (version < std::stoul(FIRST_CEREAL_CLASS_VERSION_WITH_RESOLUTION))
79     {
80         a(id, severity, timestamp, message, additionalData, associations,
81           resolved, fwVersion, updateTimestamp, eventId);
82     }
83     else
84     {
85         a(id, severity, timestamp, message, additionalData, associations,
86           resolved, fwVersion, updateTimestamp, eventId, resolution);
87     }
88 
89     e.id(id, true);
90     e.severity(severity, true);
91     e.timestamp(timestamp, true);
92     e.message(message, true);
93     e.additionalData(additionalData, true);
94     e.sdbusplus::xyz::openbmc_project::Logging::server::Entry::resolved(
95         resolved, true);
96     e.associations(associations, true);
97     e.version(fwVersion, true);
98     e.purpose(sdbusplus::xyz::openbmc_project::Software::server::Version::
99                   VersionPurpose::BMC,
100               true);
101     e.updateTimestamp(updateTimestamp, true);
102     e.eventId(eventId, true);
103     e.resolution(resolution, true);
104 }
105 
106 fs::path getEntrySerializePath(uint32_t id, const fs::path& dir)
107 {
108     return dir / std::to_string(id);
109 }
110 
111 fs::path serialize(const Entry& e, const fs::path& dir)
112 {
113     auto path = getEntrySerializePath(e.id(), dir);
114     std::ofstream os(path.c_str(), std::ios::binary);
115     cereal::BinaryOutputArchive oarchive(os);
116     oarchive(e);
117     return path;
118 }
119 
120 bool deserialize(const fs::path& path, Entry& e)
121 {
122     try
123     {
124         if (fs::exists(path))
125         {
126             std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
127             cereal::BinaryInputArchive iarchive(is);
128             iarchive(e);
129             return true;
130         }
131         return false;
132     }
133     catch (const cereal::Exception& e)
134     {
135         log<level::ERR>(e.what());
136         fs::remove(path);
137         return false;
138     }
139     catch (const std::length_error& e)
140     {
141         // Running into: USCiLab/cereal#192
142         // This may be indicating some other issue in the
143         // way vector may have been used inside the logging.
144         // possibly associations ??. But handling it here for
145         // now since we are anyway tossing the log
146         // TODO: openbmc/phosphor-logging#8
147         log<level::ERR>(e.what());
148         fs::remove(path);
149         return false;
150     }
151 }
152 
153 } // namespace logging
154 } // namespace phosphor
155