1 #include "config.h"
2 
3 #include "selutility.hpp"
4 
5 #include "types.hpp"
6 #include "utils.hpp"
7 
8 #include <ipmid/api.h>
9 
10 #include <chrono>
11 #include <phosphor-logging/elog-errors.hpp>
12 #include <vector>
13 #include <xyz/openbmc_project/Common/error.hpp>
14 
15 #if __has_include(<filesystem>)
16 #include <filesystem>
17 #elif __has_include(<experimental/filesystem>)
18 #include <experimental/filesystem>
19 namespace std
20 {
21 // splice experimental::filesystem into std
22 namespace filesystem = std::experimental::filesystem;
23 } // namespace std
24 #else
25 #error filesystem not available
26 #endif
27 
28 extern const ipmi::sensor::InvObjectIDMap invSensors;
29 using namespace phosphor::logging;
30 using InternalFailure =
31     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
32 
33 namespace ipmi
34 {
35 
36 namespace sel
37 {
38 
39 namespace internal
40 {
41 
42 GetSELEntryResponse
43     prepareSELEntry(const std::string& objPath,
44                     ipmi::sensor::InvObjectIDMap::const_iterator iter)
45 {
46     GetSELEntryResponse record{};
47 
48     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
49     auto service = ipmi::getService(bus, logEntryIntf, objPath);
50 
51     // Read all the log entry properties.
52     auto methodCall = bus.new_method_call(service.c_str(), objPath.c_str(),
53                                           propIntf, "GetAll");
54     methodCall.append(logEntryIntf);
55 
56     auto reply = bus.call(methodCall);
57     if (reply.is_method_error())
58     {
59         log<level::ERR>("Error in reading logging property entries");
60         elog<InternalFailure>();
61     }
62 
63     std::map<PropertyName, PropertyType> entryData;
64     reply.read(entryData);
65 
66     // Read Id from the log entry.
67     static constexpr auto propId = "Id";
68     auto iterId = entryData.find(propId);
69     if (iterId == entryData.end())
70     {
71         log<level::ERR>("Error in reading Id of logging entry");
72         elog<InternalFailure>();
73     }
74 
75     record.recordID = static_cast<uint16_t>(
76         sdbusplus::message::variant_ns::get<uint32_t>(iterId->second));
77 
78     // Read Timestamp from the log entry.
79     static constexpr auto propTimeStamp = "Timestamp";
80     auto iterTimeStamp = entryData.find(propTimeStamp);
81     if (iterTimeStamp == entryData.end())
82     {
83         log<level::ERR>("Error in reading Timestamp of logging entry");
84         elog<InternalFailure>();
85     }
86 
87     std::chrono::milliseconds chronoTimeStamp(
88         sdbusplus::message::variant_ns::get<uint64_t>(iterTimeStamp->second));
89     record.timeStamp = static_cast<uint32_t>(
90         std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp)
91             .count());
92 
93     static constexpr auto systemEventRecord = 0x02;
94     static constexpr auto generatorID = 0x2000;
95     static constexpr auto eventMsgRevision = 0x04;
96 
97     record.recordType = systemEventRecord;
98     record.generatorID = generatorID;
99     record.eventMsgRevision = eventMsgRevision;
100 
101     record.sensorType = iter->second.sensorType;
102     record.sensorNum = iter->second.sensorID;
103     record.eventData1 = iter->second.eventOffset;
104 
105     // Read Resolved from the log entry.
106     static constexpr auto propResolved = "Resolved";
107     auto iterResolved = entryData.find(propResolved);
108     if (iterResolved == entryData.end())
109     {
110         log<level::ERR>("Error in reading Resolved field of logging entry");
111         elog<InternalFailure>();
112     }
113 
114     static constexpr auto deassertEvent = 0x80;
115 
116     // Evaluate if the event is assertion or deassertion event
117     if (sdbusplus::message::variant_ns::get<bool>(iterResolved->second))
118     {
119         record.eventType = deassertEvent | iter->second.eventReadingType;
120     }
121     else
122     {
123         record.eventType = iter->second.eventReadingType;
124     }
125 
126     return record;
127 }
128 
129 } // namespace internal
130 
131 GetSELEntryResponse convertLogEntrytoSEL(const std::string& objPath)
132 {
133     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
134 
135     static constexpr auto assocIntf = "org.openbmc.Associations";
136     static constexpr auto assocProp = "associations";
137 
138     auto service = ipmi::getService(bus, assocIntf, objPath);
139 
140     // Read the Associations interface.
141     auto methodCall =
142         bus.new_method_call(service.c_str(), objPath.c_str(), propIntf, "Get");
143     methodCall.append(assocIntf);
144     methodCall.append(assocProp);
145 
146     auto reply = bus.call(methodCall);
147     if (reply.is_method_error())
148     {
149         log<level::ERR>("Error in reading Associations interface");
150         elog<InternalFailure>();
151     }
152 
153     using AssociationList =
154         std::vector<std::tuple<std::string, std::string, std::string>>;
155 
156     sdbusplus::message::variant<AssociationList> list;
157     reply.read(list);
158 
159     auto& assocs = sdbusplus::message::variant_ns::get<AssociationList>(list);
160 
161     /*
162      * Check if the log entry has any callout associations, if there is a
163      * callout association try to match the inventory path to the corresponding
164      * IPMI sensor.
165      */
166     for (const auto& item : assocs)
167     {
168         if (std::get<0>(item).compare(CALLOUT_FWD_ASSOCIATION) == 0)
169         {
170             auto iter = invSensors.find(std::get<2>(item));
171             if (iter == invSensors.end())
172             {
173                 iter = invSensors.find(BOARD_SENSOR);
174                 if (iter == invSensors.end())
175                 {
176                     log<level::ERR>("Motherboard sensor not found");
177                     elog<InternalFailure>();
178                 }
179             }
180 
181             return internal::prepareSELEntry(objPath, iter);
182         }
183     }
184 
185     // If there are no callout associations link the log entry to system event
186     // sensor
187     auto iter = invSensors.find(SYSTEM_SENSOR);
188     if (iter == invSensors.end())
189     {
190         log<level::ERR>("System event sensor not found");
191         elog<InternalFailure>();
192     }
193 
194     return internal::prepareSELEntry(objPath, iter);
195 }
196 
197 std::chrono::seconds getEntryTimeStamp(const std::string& objPath)
198 {
199     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
200 
201     auto service = ipmi::getService(bus, logEntryIntf, objPath);
202 
203     using namespace std::string_literals;
204     static const auto propTimeStamp = "Timestamp"s;
205 
206     auto methodCall =
207         bus.new_method_call(service.c_str(), objPath.c_str(), propIntf, "Get");
208     methodCall.append(logEntryIntf);
209     methodCall.append(propTimeStamp);
210 
211     auto reply = bus.call(methodCall);
212     if (reply.is_method_error())
213     {
214         log<level::ERR>("Error in reading Timestamp from Entry interface");
215         elog<InternalFailure>();
216     }
217 
218     sdbusplus::message::variant<uint64_t> timeStamp;
219     reply.read(timeStamp);
220 
221     std::chrono::milliseconds chronoTimeStamp(
222         sdbusplus::message::variant_ns::get<uint64_t>(timeStamp));
223 
224     return std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp);
225 }
226 
227 void readLoggingObjectPaths(ObjectPaths& paths)
228 {
229     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
230     auto depth = 0;
231     paths.clear();
232 
233     auto mapperCall = bus.new_method_call(mapperBusName, mapperObjPath,
234                                           mapperIntf, "GetSubTreePaths");
235     mapperCall.append(logBasePath);
236     mapperCall.append(depth);
237     mapperCall.append(ObjectPaths({logEntryIntf}));
238 
239     auto reply = bus.call(mapperCall);
240     if (reply.is_method_error())
241     {
242         log<level::INFO>("Error in reading logging entry object paths");
243     }
244     else
245     {
246         reply.read(paths);
247 
248         std::sort(paths.begin(), paths.end(),
249                   [](const std::string& a, const std::string& b) {
250                       namespace fs = std::filesystem;
251                       fs::path pathA(a);
252                       fs::path pathB(b);
253                       auto idA = std::stoul(pathA.filename().string());
254                       auto idB = std::stoul(pathB.filename().string());
255 
256                       return idA < idB;
257                   });
258     }
259 }
260 
261 } // namespace sel
262 
263 } // namespace ipmi
264