xref: /openbmc/ibm-logging/manager.cpp (revision f5866e704d9429a7195a20aaf52c778591531d88)
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 using sdbusplus::exception::SdBusError;
33 
34 Manager::Manager(sdbusplus::bus::bus& bus) :
35     bus(bus),
36     addMatch(bus,
37              sdbusplus::bus::match::rules::interfacesAdded() +
38                  sdbusplus::bus::match::rules::path_namespace(LOGGING_PATH),
39              std::bind(std::mem_fn(&Manager::interfaceAdded), this,
40                        std::placeholders::_1)),
41     removeMatch(bus,
42                 sdbusplus::bus::match::rules::interfacesRemoved() +
43                     sdbusplus::bus::match::rules::path_namespace(LOGGING_PATH),
44                 std::bind(std::mem_fn(&Manager::interfaceRemoved), this,
45                           std::placeholders::_1))
46 #ifdef USE_POLICY_INTERFACE
47     ,
48     policies(POLICY_JSON_PATH)
49 #endif
50 {
51     createAll();
52 }
53 
54 void Manager::createAll()
55 {
56     try
57     {
58         auto objects = getManagedObjects(bus, LOGGING_BUSNAME, LOGGING_PATH);
59 
60         for (const auto& object : objects)
61         {
62             const auto& interfaces = object.second;
63 
64             auto propertyMap = interfaces.find(LOGGING_IFACE);
65 
66             if (propertyMap != interfaces.end())
67             {
68                 createWithRestore(object.first, interfaces);
69             }
70         }
71     }
72     catch (const SdBusError& e)
73     {
74         log<level::ERR>("sdbusplus error getting logging managed objects",
75                         entry("ERROR=%s", e.what()));
76     }
77 }
78 
79 void Manager::createWithRestore(const std::string& objectPath,
80                                 const DbusInterfaceMap& interfaces)
81 {
82     createObject(objectPath, interfaces);
83 
84     restoreCalloutObjects(objectPath, interfaces);
85 }
86 
87 void Manager::create(const std::string& objectPath,
88                      const DbusInterfaceMap& interfaces)
89 {
90     createObject(objectPath, interfaces);
91 
92     createCalloutObjects(objectPath, interfaces);
93 }
94 
95 void Manager::createObject(const std::string& objectPath,
96                            const DbusInterfaceMap& interfaces)
97 {
98 #ifdef USE_POLICY_INTERFACE
99     auto logInterface = interfaces.find(LOGGING_IFACE);
100     createPolicyInterface(objectPath, logInterface->second);
101 #endif
102 }
103 
104 void Manager::erase(EntryID id)
105 {
106     fs::remove_all(getSaveDir(id));
107     childEntries.erase(id);
108     entries.erase(id);
109 }
110 
111 void Manager::addInterface(const std::string& objectPath, InterfaceType type,
112                            std::any& object)
113 {
114     auto id = getEntryID(objectPath);
115     auto entry = entries.find(id);
116 
117     if (entry == entries.end())
118     {
119         InterfaceMap interfaces;
120         interfaces.emplace(type, object);
121         entries.emplace(id, std::move(interfaces));
122     }
123     else
124     {
125         entry->second.emplace(type, object);
126     }
127 }
128 
129 void Manager::addChildInterface(const std::string& objectPath,
130                                 InterfaceType type, std::any& object)
131 {
132     auto id = getEntryID(objectPath);
133     auto entry = childEntries.find(id);
134 
135     // childEntries is:
136     // A map of error log entry IDs to:
137     //  a map of interface types to:
138     //    a vector of interface objects
139 
140     if (entry == childEntries.end())
141     {
142         ObjectList objects{object};
143         InterfaceMapMulti interfaces;
144         interfaces.emplace(type, std::move(objects));
145         childEntries.emplace(id, std::move(interfaces));
146     }
147     else
148     {
149         auto i = entry->second.find(type);
150         if (i == entry->second.end())
151         {
152             ObjectList objects{objects};
153             entry->second.emplace(type, objects);
154         }
155         else
156         {
157             i->second.emplace_back(object);
158         }
159     }
160 }
161 
162 #ifdef USE_POLICY_INTERFACE
163 void Manager::createPolicyInterface(const std::string& objectPath,
164                                     const DbusPropertyMap& properties)
165 {
166     auto values = policy::find(policies, properties);
167 
168     auto object = std::make_shared<PolicyObject>(bus, objectPath.c_str(), true);
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 
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 =
198         sdbusplus::message::variant_ns::get<AssociationsPropertyType>(
199             assocProperty->second);
200 
201     auto id = getEntryID(objectPath);
202     auto calloutNum = 0;
203     DbusSubtree subtree;
204 
205     for (const auto& association : assocValue)
206     {
207         try
208         {
209             if (std::get<forwardPos>(association) != "callout")
210             {
211                 continue;
212             }
213 
214             auto callout = std::get<endpointPos>(association);
215 
216             if (subtree.empty())
217             {
218                 subtree = getSubtree(bus, "/", 0, ASSET_IFACE);
219                 if (subtree.empty())
220                 {
221                     break;
222                 }
223             }
224 
225             auto service = getService(callout, ASSET_IFACE, subtree);
226             if (service.empty())
227             {
228                 continue;
229             }
230 
231             auto properties =
232                 getAllProperties(bus, service, callout, ASSET_IFACE);
233             if (properties.empty())
234             {
235                 continue;
236             }
237 
238             auto calloutPath = getCalloutObjectPath(objectPath, calloutNum);
239 
240             auto object = std::make_shared<Callout>(
241                 bus, calloutPath, callout, calloutNum,
242                 getLogTimestamp(interfaces), properties);
243 
244             auto dir = getCalloutSaveDir(id);
245             if (!fs::exists(dir))
246             {
247                 fs::create_directories(dir);
248             }
249             object->serialize(dir);
250 
251             std::any anyObject = object;
252             addChildInterface(objectPath, InterfaceType::CALLOUT, anyObject);
253             calloutNum++;
254         }
255         catch (const SdBusError& e)
256         {
257             log<level::ERR>("sdbusplus exception", entry("ERROR=%s", e.what()));
258         }
259     }
260 }
261 
262 void Manager::restoreCalloutObjects(const std::string& objectPath,
263                                     const DbusInterfaceMap& interfaces)
264 {
265     auto saveDir = getCalloutSaveDir(getEntryID(objectPath));
266 
267     if (!fs::exists(saveDir))
268     {
269         return;
270     }
271 
272     size_t id;
273     for (auto& f : fs::directory_iterator(saveDir))
274     {
275         try
276         {
277             id = std::stoul(f.path().filename());
278         }
279         catch (std::exception& e)
280         {
281             log<level::ERR>("Invalid IBM logging callout save file. Deleting",
282                             entry("FILE=%s", f.path().c_str()));
283             fs::remove(f.path());
284             continue;
285         }
286 
287         auto path = getCalloutObjectPath(objectPath, id);
288         auto callout = std::make_shared<Callout>(bus, path, id,
289                                                  getLogTimestamp(interfaces));
290         if (callout->deserialize(saveDir))
291         {
292             callout->emit_object_added();
293             std::any anyObject = callout;
294             addChildInterface(objectPath, InterfaceType::CALLOUT, anyObject);
295         }
296     }
297 }
298 
299 void Manager::interfaceAdded(sdbusplus::message::message& msg)
300 {
301     sdbusplus::message::object_path path;
302     DbusInterfaceMap interfaces;
303 
304     msg.read(path, interfaces);
305 
306     // Find the Logging.Entry interface with all of its properties
307     // to pass to create().
308     if (interfaces.find(LOGGING_IFACE) != interfaces.end())
309     {
310         create(path, interfaces);
311     }
312 }
313 
314 uint64_t Manager::getLogTimestamp(const DbusInterfaceMap& interfaces)
315 {
316     auto interface = interfaces.find(LOGGING_IFACE);
317     if (interface != interfaces.end())
318     {
319         auto property = interface->second.find("Timestamp");
320         if (property != interface->second.end())
321         {
322             return sdbusplus::message::variant_ns::get<uint64_t>(
323                 property->second);
324         }
325     }
326 
327     return 0;
328 }
329 
330 fs::path Manager::getSaveDir(EntryID id)
331 {
332     return fs::path{ERRLOG_PERSIST_PATH} / std::to_string(id);
333 }
334 
335 fs::path Manager::getCalloutSaveDir(EntryID id)
336 {
337     return getSaveDir(id) / "callouts";
338 }
339 
340 std::string Manager::getCalloutObjectPath(const std::string& objectPath,
341                                           uint32_t calloutNum)
342 {
343     return fs::path{objectPath} / "callouts" / std::to_string(calloutNum);
344 }
345 
346 void Manager::interfaceRemoved(sdbusplus::message::message& msg)
347 {
348     sdbusplus::message::object_path path;
349     DbusInterfaceList interfaces;
350 
351     msg.read(path, interfaces);
352 
353     // If the Logging.Entry interface was removed, then remove
354     // our object
355 
356     auto i = std::find(interfaces.begin(), interfaces.end(), LOGGING_IFACE);
357 
358     if (i != interfaces.end())
359     {
360         erase(getEntryID(path));
361     }
362 }
363 } // namespace logging
364 } // namespace ibm
365