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