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