1 #include "config.h"
2 
3 #include "elog_watch.hpp"
4 
5 #include "dump_internal.hpp"
6 #include "dump_serialize.hpp"
7 #include "errors_map.hpp"
8 #include "xyz/openbmc_project/Dump/Create/error.hpp"
9 
10 #include <fmt/core.h>
11 
12 #include <cereal/cereal.hpp>
13 #include <phosphor-logging/elog.hpp>
14 #include <sdbusplus/exception.hpp>
15 
16 #include <fstream>
17 
18 // Register class version with Cereal
19 CEREAL_CLASS_VERSION(phosphor::dump::elog::Watch, CLASS_VERSION)
20 
21 namespace phosphor
22 {
23 namespace dump
24 {
25 namespace elog
26 {
27 
28 using namespace phosphor::logging;
29 constexpr auto LOG_PATH = "/xyz/openbmc_project/logging";
30 using Message = std::string;
31 using Attributes = std::variant<Message>;
32 using AttributeName = std::string;
33 using AttributeMap = std::map<AttributeName, Attributes>;
34 using PropertyName = std::string;
35 using PropertyMap = std::map<PropertyName, AttributeMap>;
36 
37 Watch::Watch(sdbusplus::bus::bus& bus, IMgr& iMgr) :
38     iMgr(iMgr),
39     addMatch(bus,
40              sdbusplus::bus::match::rules::interfacesAdded() +
41                  sdbusplus::bus::match::rules::path_namespace(OBJ_LOGGING),
42              std::bind(std::mem_fn(&Watch::addCallback), this,
43                        std::placeholders::_1)),
44     delMatch(bus,
45              sdbusplus::bus::match::rules::interfacesRemoved() +
46                  sdbusplus::bus::match::rules::path_namespace(OBJ_LOGGING),
47              std::bind(std::mem_fn(&Watch::delCallback), this,
48                        std::placeholders::_1))
49 {
50 
51     std::filesystem::path file(ELOG_ID_PERSIST_PATH);
52     if (std::filesystem::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 QuotaExceeded =
64         sdbusplus::xyz::openbmc_project::Dump::Create::Error::QuotaExceeded;
65 
66     sdbusplus::message::object_path objectPath;
67     PropertyMap propertyMap;
68     try
69     {
70         msg.read(objectPath, propertyMap);
71     }
72     catch (const sdbusplus::exception::exception& e)
73     {
74         log<level::ERR>(
75             fmt::format(
76                 "Failed to parse elog add signal, errormsg({}), REPLY_SIG({})",
77                 e.what(), msg.get_signature())
78                 .c_str());
79         return;
80     }
81 
82     std::size_t found = objectPath.str.find("entry");
83     if (found == std::string::npos)
84     {
85         // Not a new error entry skip
86         return;
87     }
88 
89     auto eId = getEid(objectPath);
90 
91     auto search = elogList.find(eId);
92     if (search != elogList.end())
93     {
94         // elog exists in the list, Skip the dump
95         return;
96     }
97 
98     auto iter = propertyMap.find("xyz.openbmc_project.Logging.Entry");
99     if (iter == propertyMap.end())
100     {
101         return;
102     }
103 
104     auto attr = iter->second.find("Message");
105     if (attr == iter->second.end())
106     {
107         return;
108     }
109 
110     auto& data = std::get<PropertyName>(attr->second);
111     if (data.empty())
112     {
113         // No Message skip
114         return;
115     }
116 
117     EType errorType;
118     for (const auto& [type, errorList] : errorMap)
119     {
120         auto error = std::find(errorList.begin(), errorList.end(), data);
121         if (error != errorList.end())
122         {
123             errorType = type;
124             break;
125         }
126     }
127 
128     // error not supported in the configuration
129     if (errorType.empty())
130     {
131         return;
132     }
133 
134     std::vector<std::string> fullPaths;
135     fullPaths.push_back(objectPath);
136 
137     try
138     {
139         // Save the elog information. This is to avoid dump requests
140         // in elog restore path.
141         elogList.insert(eId);
142 
143         phosphor::dump::elog::serialize(elogList);
144 
145         auto item = std::find_if(
146             phosphor::dump::bmc::TypeMap.begin(),
147             phosphor::dump::bmc::TypeMap.end(),
148             [errorType](const auto& err) { return (err.second == errorType); });
149         if (item != phosphor::dump::bmc::TypeMap.end())
150         {
151             iMgr.IMgr::create((*item).first, fullPaths);
152         }
153     }
154     catch (QuotaExceeded& e)
155     {
156         // No action now
157     }
158     return;
159 }
160 
161 void Watch::delCallback(sdbusplus::message::message& msg)
162 {
163     sdbusplus::message::object_path objectPath;
164     try
165     {
166         msg.read(objectPath);
167     }
168     catch (const sdbusplus::exception::exception& e)
169     {
170         log<level::ERR>(
171             fmt::format(
172                 "Failed to parse elog del signal, errormsg({}), REPLY_SIG({})",
173                 e.what(), msg.get_signature())
174                 .c_str());
175         return;
176     }
177 
178     std::size_t found = objectPath.str.find("entry");
179     if (found == std::string::npos)
180     {
181         // Not a error entry so skip
182         return;
183     }
184 
185     // Get elog id
186     auto eId = getEid(objectPath);
187 
188     // Delete the elog entry from the list and serialize
189     auto search = elogList.find(eId);
190     if (search != elogList.end())
191     {
192         elogList.erase(search);
193         phosphor::dump::elog::serialize(elogList);
194     }
195 }
196 
197 } // namespace elog
198 } // namespace dump
199 } // namespace phosphor
200