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