xref: /openbmc/ibm-logging/manager.cpp (revision bf9ea8ad)
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 
33 Manager::Manager(sdbusplus::bus::bus& 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 
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::exception& e)
72     {
73         log<level::ERR>("sdbusplus error getting logging managed objects",
74                         entry("ERROR=%s", e.what()));
75     }
76 }
77 
78 void Manager::createWithRestore(const std::string& objectPath,
79                                 const DbusInterfaceMap& interfaces)
80 {
81     createObject(objectPath, interfaces);
82 
83     restoreCalloutObjects(objectPath, interfaces);
84 }
85 
86 void Manager::create(const std::string& objectPath,
87                      const DbusInterfaceMap& interfaces)
88 {
89     createObject(objectPath, interfaces);
90 
91     createCalloutObjects(objectPath, interfaces);
92 }
93 
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 
103 void Manager::erase(EntryID id)
104 {
105     fs::remove_all(getSaveDir(id));
106     childEntries.erase(id);
107     entries.erase(id);
108 }
109 
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 
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{objects};
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
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>(bus, objectPath.c_str(), true);
168 
169     object->eventID(std::get<policy::EIDField>(values));
170     object->description(std::get<policy::MsgField>(values));
171 
172     object->emit_object_added();
173 
174     std::any anyObject = object;
175 
176     addInterface(objectPath, InterfaceType::POLICY, anyObject);
177 }
178 #endif
179 
180 void Manager::createCalloutObjects(const std::string& objectPath,
181                                    const DbusInterfaceMap& interfaces)
182 {
183     // Use the associations property in the org.openbmc.Associations
184     // interface to find any callouts.  Then grab all properties on
185     // the Asset interface for that object in the inventory to use
186     // in our callout objects.
187 
188     auto associations = interfaces.find(ASSOC_IFACE);
189     if (associations == interfaces.end())
190     {
191         return;
192     }
193 
194     const auto& properties = associations->second;
195     auto assocProperty = properties.find("Associations");
196     auto assocValue = std::get<AssociationsPropertyType>(assocProperty->second);
197 
198     auto id = getEntryID(objectPath);
199     auto calloutNum = 0;
200     DbusSubtree subtree;
201 
202     for (const auto& association : assocValue)
203     {
204         try
205         {
206             if (std::get<forwardPos>(association) != "callout")
207             {
208                 continue;
209             }
210 
211             auto callout = std::get<endpointPos>(association);
212 
213             if (subtree.empty())
214             {
215                 subtree = getSubtree(bus, "/", 0, ASSET_IFACE);
216                 if (subtree.empty())
217                 {
218                     break;
219                 }
220             }
221 
222             auto service = getService(callout, ASSET_IFACE, subtree);
223             if (service.empty())
224             {
225                 continue;
226             }
227 
228             auto properties =
229                 getAllProperties(bus, service, callout, ASSET_IFACE);
230             if (properties.empty())
231             {
232                 continue;
233             }
234 
235             auto calloutPath = getCalloutObjectPath(objectPath, calloutNum);
236 
237             auto object = std::make_shared<Callout>(
238                 bus, calloutPath, callout, calloutNum,
239                 getLogTimestamp(interfaces), properties);
240 
241             auto dir = getCalloutSaveDir(id);
242             if (!fs::exists(dir))
243             {
244                 fs::create_directories(dir);
245             }
246             object->serialize(dir);
247 
248             std::any anyObject = object;
249             addChildInterface(objectPath, InterfaceType::CALLOUT, anyObject);
250             calloutNum++;
251         }
252         catch (const sdbusplus::exception::exception& e)
253         {
254             log<level::ERR>("sdbusplus exception", entry("ERROR=%s", e.what()));
255         }
256     }
257 }
258 
259 void Manager::restoreCalloutObjects(const std::string& objectPath,
260                                     const DbusInterfaceMap& interfaces)
261 {
262     auto saveDir = getCalloutSaveDir(getEntryID(objectPath));
263 
264     if (!fs::exists(saveDir))
265     {
266         return;
267     }
268 
269     size_t id;
270     for (auto& f : fs::directory_iterator(saveDir))
271     {
272         try
273         {
274             id = std::stoul(f.path().filename());
275         }
276         catch (const std::exception& e)
277         {
278             log<level::ERR>("Invalid IBM logging callout save file. Deleting",
279                             entry("FILE=%s", f.path().c_str()));
280             fs::remove(f.path());
281             continue;
282         }
283 
284         auto path = getCalloutObjectPath(objectPath, id);
285         auto callout = std::make_shared<Callout>(bus, path, id,
286                                                  getLogTimestamp(interfaces));
287         if (callout->deserialize(saveDir))
288         {
289             callout->emit_object_added();
290             std::any anyObject = callout;
291             addChildInterface(objectPath, InterfaceType::CALLOUT, anyObject);
292         }
293     }
294 }
295 
296 void Manager::interfaceAdded(sdbusplus::message::message& msg)
297 {
298     sdbusplus::message::object_path path;
299     DbusInterfaceMap interfaces;
300 
301     msg.read(path, interfaces);
302 
303     // Find the Logging.Entry interface with all of its properties
304     // to pass to create().
305     if (interfaces.find(LOGGING_IFACE) != interfaces.end())
306     {
307         create(path, interfaces);
308     }
309 }
310 
311 uint64_t Manager::getLogTimestamp(const DbusInterfaceMap& interfaces)
312 {
313     auto interface = interfaces.find(LOGGING_IFACE);
314     if (interface != interfaces.end())
315     {
316         auto property = interface->second.find("Timestamp");
317         if (property != interface->second.end())
318         {
319             return std::get<uint64_t>(property->second);
320         }
321     }
322 
323     return 0;
324 }
325 
326 fs::path Manager::getSaveDir(EntryID id)
327 {
328     return fs::path{ERRLOG_PERSIST_PATH} / std::to_string(id);
329 }
330 
331 fs::path Manager::getCalloutSaveDir(EntryID id)
332 {
333     return getSaveDir(id) / "callouts";
334 }
335 
336 std::string Manager::getCalloutObjectPath(const std::string& objectPath,
337                                           uint32_t calloutNum)
338 {
339     return fs::path{objectPath} / "callouts" / std::to_string(calloutNum);
340 }
341 
342 void Manager::interfaceRemoved(sdbusplus::message::message& msg)
343 {
344     sdbusplus::message::object_path path;
345     DbusInterfaceList interfaces;
346 
347     msg.read(path, interfaces);
348 
349     // If the Logging.Entry interface was removed, then remove
350     // our object
351 
352     auto i = std::find(interfaces.begin(), interfaces.end(), LOGGING_IFACE);
353 
354     if (i != interfaces.end())
355     {
356         erase(getEntryID(path));
357     }
358 }
359 } // namespace logging
360 } // namespace ibm
361