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