1 #include <cereal/cereal.hpp>
2 #include <phosphor-logging/elog.hpp>
3 #include <sdbusplus/exception.hpp>
4 
5 #include "elog_watch.hpp"
6 #include "dump_internal.hpp"
7 #include "xyz/openbmc_project/Dump/Create/error.hpp"
8 #include "dump_serialize.hpp"
9 #include "config.h"
10 
11 // Register class version with Cereal
12 CEREAL_CLASS_VERSION(phosphor::dump::elog::Watch, CLASS_VERSION);
13 
14 namespace phosphor
15 {
16 namespace dump
17 {
18 namespace elog
19 {
20 
21 using namespace phosphor::logging;
22 constexpr auto LOG_PATH = "/xyz/openbmc_project/logging";
23 constexpr auto INTERNAL_FAILURE =
24     "xyz.openbmc_project.Common.Error.InternalFailure";
25 using Message = std::string;
26 using Attributes = sdbusplus::message::variant<Message>;
27 using AttributeName = std::string;
28 using AttributeMap = std::map<AttributeName, Attributes>;
29 using PropertyName = std::string;
30 using PropertyMap = std::map<PropertyName, AttributeMap>;
31 using LogEntryMsg = std::pair<sdbusplus::message::object_path, PropertyMap>;
32 
33 Watch::Watch(sdbusplus::bus::bus& bus, IMgr& iMgr):
34     iMgr(iMgr),
35     addMatch(
36         bus,
37         sdbusplus::bus::match::rules::interfacesAdded() +
38         sdbusplus::bus::match::rules::path_namespace(
39             OBJ_LOGGING),
40         std::bind(std::mem_fn(&Watch::addCallback),
41                   this, std::placeholders::_1)),
42     delMatch(
43         bus,
44         sdbusplus::bus::match::rules::interfacesRemoved() +
45         sdbusplus::bus::match::rules::path_namespace(
46             OBJ_LOGGING),
47         std::bind(std::mem_fn(&Watch::delCallback),
48                   this, std::placeholders::_1))
49 {
50 
51     fs::path file(ELOG_ID_PERSIST_PATH);
52     if (fs::exists(file))
53     {
54         if (!deserialize(ELOG_ID_PERSIST_PATH, elogList))
55         {
56             log<level::ERR>("Error occurred during error id deserialize");
57         }
58     }
59 }
60 
61 void Watch::addCallback(sdbusplus::message::message& msg)
62 {
63     using Type =
64         sdbusplus::xyz::openbmc_project::Dump::Internal::server::Create::Type;
65     using QuotaExceeded =
66         sdbusplus::xyz::openbmc_project::Dump::Create::Error::QuotaExceeded;
67 
68     LogEntryMsg logEntry;
69     try
70     {
71         msg.read(logEntry);
72     }
73     catch (const sdbusplus::exception::SdBusError& e)
74     {
75         log<level::ERR>("Failed to parse elog add signal",
76                         entry("ERROR=%s", e.what()),
77                         entry("REPLY_SIG=%s", msg.get_signature()));
78         return;
79     }
80 
81     std::string objectPath(std::move(logEntry.first));
82 
83     std::size_t found = objectPath.find("entry");
84     if (found == std::string::npos)
85     {
86         //Not a new error entry skip
87         return;
88     }
89 
90     auto eId = getEid(objectPath);
91 
92     auto search = elogList.find(eId);
93     if (search != elogList.end())
94     {
95         //elog exists in the list, Skip the dump
96         return;
97     }
98 
99     auto iter = logEntry.second.find("xyz.openbmc_project.Logging.Entry");
100     if (iter == logEntry.second.end())
101     {
102         return;
103     }
104 
105     auto attr = iter->second.find("Message");
106     if (attr == iter->second.end())
107     {
108         return;
109     }
110 
111     auto& data =
112         sdbusplus::message::variant_ns::get<PropertyName>(attr->second);
113     if (data.empty())
114     {
115         //No Message skip
116         return;
117     }
118 
119     if (data != INTERNAL_FAILURE)
120     {
121         //Not a InternalFailure, skip
122         return;
123     }
124 
125     std::vector<std::string> fullPaths;
126     fullPaths.push_back(objectPath);
127 
128     try
129     {
130         //Save the elog information. This is to avoid dump requests
131         //in elog restore path.
132         elogList.insert(eId);
133 
134         phosphor::dump::elog::serialize(elogList);
135 
136         //Call internal create function to initiate dump
137         iMgr.IMgr::create(Type::InternalFailure, fullPaths);
138     }
139     catch (QuotaExceeded& e)
140     {
141         //No action now
142     }
143     return;
144 }
145 
146 void Watch::delCallback(sdbusplus::message::message& msg)
147 {
148     sdbusplus::message::object_path logEntry;
149     try
150     {
151         msg.read(logEntry);
152     }
153     catch (const sdbusplus::exception::SdBusError& e)
154     {
155         log<level::ERR>("Failed to parse elog del signal",
156                         entry("ERROR=%s", e.what()),
157                         entry("REPLY_SIG=%s", msg.get_signature()));
158         return;
159     }
160 
161     //Get elog entry message string.
162     std::string objectPath(logEntry);
163 
164     //Get elog id
165     auto eId = getEid(objectPath);
166 
167     //Delete the elog entry from the list and serialize
168     auto search = elogList.find(eId);
169     if (search != elogList.end())
170     {
171         elogList.erase(search);
172         phosphor::dump::elog::serialize(elogList);
173     }
174 }
175 
176 }//namespace elog
177 }//namespace dump
178 }//namespace phosphor
179