1 /**
2 * Copyright © 2018 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "config.h"
17
18 #include "manager.hpp"
19
20 #include "callout.hpp"
21 #include "policy_find.hpp"
22
23 #include <phosphor-logging/log.hpp>
24
25 namespace ibm
26 {
27 namespace logging
28 {
29
30 namespace fs = std::experimental::filesystem;
31 using namespace phosphor::logging;
32
Manager(sdbusplus::bus_t & bus)33 Manager::Manager(sdbusplus::bus_t& bus) :
34 bus(bus),
35 addMatch(bus,
36 sdbusplus::bus::match::rules::interfacesAdded() +
37 sdbusplus::bus::match::rules::path_namespace(LOGGING_PATH),
38 std::bind(std::mem_fn(&Manager::interfaceAdded), this,
39 std::placeholders::_1)),
40 removeMatch(bus,
41 sdbusplus::bus::match::rules::interfacesRemoved() +
42 sdbusplus::bus::match::rules::path_namespace(LOGGING_PATH),
43 std::bind(std::mem_fn(&Manager::interfaceRemoved), this,
44 std::placeholders::_1))
45 #ifdef USE_POLICY_INTERFACE
46 ,
47 policies(POLICY_JSON_PATH)
48 #endif
49 {
50 createAll();
51 }
52
createAll()53 void Manager::createAll()
54 {
55 try
56 {
57 auto objects = getManagedObjects(bus, LOGGING_BUSNAME, LOGGING_PATH);
58
59 for (const auto& object : objects)
60 {
61 const auto& interfaces = object.second;
62
63 auto propertyMap = interfaces.find(LOGGING_IFACE);
64
65 if (propertyMap != interfaces.end())
66 {
67 createWithRestore(object.first, interfaces);
68 }
69 }
70 }
71 catch (const sdbusplus::exception_t& e)
72 {
73 log<level::ERR>("sdbusplus error getting logging managed objects",
74 entry("ERROR=%s", e.what()));
75 }
76 }
77
createWithRestore(const std::string & objectPath,const DbusInterfaceMap & interfaces)78 void Manager::createWithRestore(const std::string& objectPath,
79 const DbusInterfaceMap& interfaces)
80 {
81 createObject(objectPath, interfaces);
82
83 restoreCalloutObjects(objectPath, interfaces);
84 }
85
create(const std::string & objectPath,const DbusInterfaceMap & interfaces)86 void Manager::create(const std::string& objectPath,
87 const DbusInterfaceMap& interfaces)
88 {
89 createObject(objectPath, interfaces);
90
91 createCalloutObjects(objectPath, interfaces);
92 }
93
createObject(const std::string & objectPath,const DbusInterfaceMap & interfaces)94 void Manager::createObject(const std::string& objectPath,
95 const DbusInterfaceMap& interfaces)
96 {
97 #ifdef USE_POLICY_INTERFACE
98 auto logInterface = interfaces.find(LOGGING_IFACE);
99 createPolicyInterface(objectPath, logInterface->second);
100 #endif
101 }
102
erase(EntryID id)103 void Manager::erase(EntryID id)
104 {
105 fs::remove_all(getSaveDir(id));
106 childEntries.erase(id);
107 entries.erase(id);
108 }
109
addInterface(const std::string & objectPath,InterfaceType type,std::any & object)110 void Manager::addInterface(const std::string& objectPath, InterfaceType type,
111 std::any& object)
112 {
113 auto id = getEntryID(objectPath);
114 auto entry = entries.find(id);
115
116 if (entry == entries.end())
117 {
118 InterfaceMap interfaces;
119 interfaces.emplace(type, object);
120 entries.emplace(id, std::move(interfaces));
121 }
122 else
123 {
124 entry->second.emplace(type, object);
125 }
126 }
127
addChildInterface(const std::string & objectPath,InterfaceType type,std::any & object)128 void Manager::addChildInterface(const std::string& objectPath,
129 InterfaceType type, std::any& object)
130 {
131 auto id = getEntryID(objectPath);
132 auto entry = childEntries.find(id);
133
134 // childEntries is:
135 // A map of error log entry IDs to:
136 // a map of interface types to:
137 // a vector of interface objects
138
139 if (entry == childEntries.end())
140 {
141 ObjectList objects{object};
142 InterfaceMapMulti interfaces;
143 interfaces.emplace(type, std::move(objects));
144 childEntries.emplace(id, std::move(interfaces));
145 }
146 else
147 {
148 auto i = entry->second.find(type);
149 if (i == entry->second.end())
150 {
151 ObjectList objects{object};
152 entry->second.emplace(type, objects);
153 }
154 else
155 {
156 i->second.emplace_back(object);
157 }
158 }
159 }
160
161 #ifdef USE_POLICY_INTERFACE
createPolicyInterface(const std::string & objectPath,const DbusPropertyMap & properties)162 void Manager::createPolicyInterface(const std::string& objectPath,
163 const DbusPropertyMap& properties)
164 {
165 auto values = policy::find(policies, properties);
166
167 auto object = std::make_shared<PolicyObject>(
168 bus, objectPath.c_str(), PolicyObject::action::defer_emit);
169
170 object->eventID(std::get<policy::EIDField>(values));
171 object->description(std::get<policy::MsgField>(values));
172
173 object->emit_object_added();
174
175 std::any anyObject = object;
176
177 addInterface(objectPath, InterfaceType::POLICY, anyObject);
178 }
179 #endif
180
createCalloutObjects(const std::string & objectPath,const DbusInterfaceMap & interfaces)181 void Manager::createCalloutObjects(const std::string& objectPath,
182 const DbusInterfaceMap& interfaces)
183 {
184 // Use the associations property in the org.openbmc.Associations
185 // interface to find any callouts. Then grab all properties on
186 // the Asset interface for that object in the inventory to use
187 // in our callout objects.
188
189 auto associations = interfaces.find(ASSOC_IFACE);
190 if (associations == interfaces.end())
191 {
192 return;
193 }
194
195 const auto& properties = associations->second;
196 auto assocProperty = properties.find("Associations");
197 auto assocValue = std::get<AssociationsPropertyType>(assocProperty->second);
198
199 auto id = getEntryID(objectPath);
200 auto calloutNum = 0;
201 DbusSubtree subtree;
202
203 for (const auto& association : assocValue)
204 {
205 try
206 {
207 if (std::get<forwardPos>(association) != "callout")
208 {
209 continue;
210 }
211
212 auto callout = std::get<endpointPos>(association);
213
214 if (subtree.empty())
215 {
216 subtree = getSubtree(bus, "/", 0, ASSET_IFACE);
217 if (subtree.empty())
218 {
219 break;
220 }
221 }
222
223 auto service = getService(callout, ASSET_IFACE, subtree);
224 if (service.empty())
225 {
226 continue;
227 }
228
229 auto properties = getAllProperties(bus, service, callout,
230 ASSET_IFACE);
231 if (properties.empty())
232 {
233 continue;
234 }
235
236 auto calloutPath = getCalloutObjectPath(objectPath, calloutNum);
237
238 auto object = std::make_shared<Callout>(
239 bus, calloutPath, callout, calloutNum,
240 getLogTimestamp(interfaces), properties);
241
242 auto dir = getCalloutSaveDir(id);
243 if (!fs::exists(dir))
244 {
245 fs::create_directories(dir);
246 }
247 object->serialize(dir);
248
249 std::any anyObject = object;
250 addChildInterface(objectPath, InterfaceType::CALLOUT, anyObject);
251 calloutNum++;
252 }
253 catch (const sdbusplus::exception_t& e)
254 {
255 log<level::ERR>("sdbusplus exception", entry("ERROR=%s", e.what()));
256 }
257 }
258 }
259
restoreCalloutObjects(const std::string & objectPath,const DbusInterfaceMap & interfaces)260 void Manager::restoreCalloutObjects(const std::string& objectPath,
261 const DbusInterfaceMap& interfaces)
262 {
263 auto saveDir = getCalloutSaveDir(getEntryID(objectPath));
264
265 if (!fs::exists(saveDir))
266 {
267 return;
268 }
269
270 size_t id;
271 for (auto& f : fs::directory_iterator(saveDir))
272 {
273 try
274 {
275 id = std::stoul(f.path().filename());
276 }
277 catch (const std::exception& e)
278 {
279 log<level::ERR>("Invalid IBM logging callout save file. Deleting",
280 entry("FILE=%s", f.path().c_str()));
281 fs::remove(f.path());
282 continue;
283 }
284
285 auto path = getCalloutObjectPath(objectPath, id);
286 auto callout = std::make_shared<Callout>(bus, path, id,
287 getLogTimestamp(interfaces));
288 if (callout->deserialize(saveDir))
289 {
290 callout->emit_object_added();
291 std::any anyObject = callout;
292 addChildInterface(objectPath, InterfaceType::CALLOUT, anyObject);
293 }
294 }
295 }
296
interfaceAdded(sdbusplus::message_t & msg)297 void Manager::interfaceAdded(sdbusplus::message_t& msg)
298 {
299 sdbusplus::message::object_path path;
300 DbusInterfaceMap interfaces;
301
302 msg.read(path, interfaces);
303
304 // Find the Logging.Entry interface with all of its properties
305 // to pass to create().
306 if (interfaces.find(LOGGING_IFACE) != interfaces.end())
307 {
308 create(path, interfaces);
309 }
310 }
311
getLogTimestamp(const DbusInterfaceMap & interfaces)312 uint64_t Manager::getLogTimestamp(const DbusInterfaceMap& interfaces)
313 {
314 auto interface = interfaces.find(LOGGING_IFACE);
315 if (interface != interfaces.end())
316 {
317 auto property = interface->second.find("Timestamp");
318 if (property != interface->second.end())
319 {
320 return std::get<uint64_t>(property->second);
321 }
322 }
323
324 return 0;
325 }
326
getSaveDir(EntryID id)327 fs::path Manager::getSaveDir(EntryID id)
328 {
329 return fs::path{ERRLOG_PERSIST_PATH} / std::to_string(id);
330 }
331
getCalloutSaveDir(EntryID id)332 fs::path Manager::getCalloutSaveDir(EntryID id)
333 {
334 return getSaveDir(id) / "callouts";
335 }
336
getCalloutObjectPath(const std::string & objectPath,uint32_t calloutNum)337 std::string Manager::getCalloutObjectPath(const std::string& objectPath,
338 uint32_t calloutNum)
339 {
340 return fs::path{objectPath} / "callouts" / std::to_string(calloutNum);
341 }
342
interfaceRemoved(sdbusplus::message_t & msg)343 void Manager::interfaceRemoved(sdbusplus::message_t& msg)
344 {
345 sdbusplus::message::object_path path;
346 DbusInterfaceList interfaces;
347
348 msg.read(path, interfaces);
349
350 // If the Logging.Entry interface was removed, then remove
351 // our object
352
353 auto i = std::find(interfaces.begin(), interfaces.end(), LOGGING_IFACE);
354
355 if (i != interfaces.end())
356 {
357 erase(getEntryID(path));
358 }
359 }
360 } // namespace logging
361 } // namespace ibm
362