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