blob: 2503e93f4b43026c8c1be7829b155706d9476f86 [file] [log] [blame]
Matt Spinlere0017eb2018-03-27 11:17:38 -05001/**
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 */
Matt Spinler60f53d82018-05-23 13:23:09 -050016#include <phosphor-logging/log.hpp>
Matt Spinler52ee71b2018-05-23 13:18:55 -050017#include "callout.hpp"
Matt Spinlere0017eb2018-03-27 11:17:38 -050018#include "config.h"
19#include "manager.hpp"
Matt Spinler4a6ea6a2018-03-27 14:25:12 -050020#include "policy_find.hpp"
Matt Spinlere0017eb2018-03-27 11:17:38 -050021
22namespace ibm
23{
24namespace logging
25{
26
Matt Spinler52ee71b2018-05-23 13:18:55 -050027namespace fs = std::experimental::filesystem;
28
Matt Spinlere0017eb2018-03-27 11:17:38 -050029Manager::Manager(sdbusplus::bus::bus& bus) :
Matt Spinler259e7272018-03-29 10:57:17 -050030 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,
Matt Spinler055da3b2018-05-09 15:51:20 -050035 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))
Matt Spinler743e5822018-03-27 13:44:50 -050041#ifdef USE_POLICY_INTERFACE
Matt Spinler259e7272018-03-29 10:57:17 -050042 ,
43 policies(POLICY_JSON_PATH)
Matt Spinler743e5822018-03-27 13:44:50 -050044#endif
Matt Spinlere0017eb2018-03-27 11:17:38 -050045{
Matt Spinler54bfa7e2018-03-27 11:28:59 -050046 createAll();
47}
48
49void Manager::createAll()
50{
Matt Spinler259e7272018-03-29 10:57:17 -050051 auto objects = getManagedObjects(bus, LOGGING_BUSNAME, LOGGING_PATH);
Matt Spinler54bfa7e2018-03-27 11:28:59 -050052
53 for (const auto& object : objects)
54 {
55 const auto& interfaces = object.second;
56
Matt Spinlere6a51592018-05-23 12:47:10 -050057 auto propertyMap = interfaces.find(LOGGING_IFACE);
Matt Spinler54bfa7e2018-03-27 11:28:59 -050058
59 if (propertyMap != interfaces.end())
60 {
Matt Spinlere6a51592018-05-23 12:47:10 -050061 createWithRestore(object.first, interfaces);
Matt Spinler54bfa7e2018-03-27 11:28:59 -050062 }
63 }
64}
65
Matt Spinlere6a51592018-05-23 12:47:10 -050066void Manager::createWithRestore(const std::string& objectPath,
67 const DbusInterfaceMap& interfaces)
Matt Spinler54bfa7e2018-03-27 11:28:59 -050068{
Matt Spinlere6a51592018-05-23 12:47:10 -050069 createObject(objectPath, interfaces);
Matt Spinler4a6ea6a2018-03-27 14:25:12 -050070
Matt Spinler60f53d82018-05-23 13:23:09 -050071 restoreCalloutObjects(objectPath, interfaces);
Matt Spinlere6a51592018-05-23 12:47:10 -050072}
73
74void Manager::create(const std::string& objectPath,
75 const DbusInterfaceMap& interfaces)
76{
77 createObject(objectPath, interfaces);
78
Matt Spinler52ee71b2018-05-23 13:18:55 -050079 createCalloutObjects(objectPath, interfaces);
Matt Spinlere6a51592018-05-23 12:47:10 -050080}
81
82void Manager::createObject(const std::string& objectPath,
83 const DbusInterfaceMap& interfaces)
84{
Matt Spinler4a6ea6a2018-03-27 14:25:12 -050085#ifdef USE_POLICY_INTERFACE
Matt Spinlere6a51592018-05-23 12:47:10 -050086 auto logInterface = interfaces.find(LOGGING_IFACE);
87 createPolicyInterface(objectPath, logInterface->second);
Matt Spinler4a6ea6a2018-03-27 14:25:12 -050088#endif
Matt Spinlere0017eb2018-03-27 11:17:38 -050089}
90
Matt Spinlera1390352018-05-23 10:48:19 -050091void Manager::erase(EntryID id)
92{
Matt Spinler52ee71b2018-05-23 13:18:55 -050093 fs::remove_all(getSaveDir(id));
Matt Spinler677143b2018-05-23 10:53:27 -050094 childEntries.erase(id);
Matt Spinlera1390352018-05-23 10:48:19 -050095 entries.erase(id);
96}
97
Matt Spinler491fc6f2018-04-23 11:00:52 -050098void 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
Matt Spinler677143b2018-05-23 10:53:27 -0500116void 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
Matt Spinler4a6ea6a2018-03-27 14:25:12 -0500150#ifdef USE_POLICY_INTERFACE
Matt Spinler259e7272018-03-29 10:57:17 -0500151void Manager::createPolicyInterface(const std::string& objectPath,
152 const DbusPropertyMap& properties)
Matt Spinler4a6ea6a2018-03-27 14:25:12 -0500153{
154 auto values = policy::find(policies, properties);
155
Matt Spinler259e7272018-03-29 10:57:17 -0500156 auto object = std::make_shared<PolicyObject>(bus, objectPath.c_str(), true);
Matt Spinler4a6ea6a2018-03-27 14:25:12 -0500157
158 object->eventID(std::get<policy::EIDField>(values));
159 object->description(std::get<policy::MsgField>(values));
160
Matt Spinler9bea4ea2018-05-09 15:53:04 -0500161 object->emit_object_added();
162
Matt Spinler491fc6f2018-04-23 11:00:52 -0500163 std::experimental::any anyObject = object;
Matt Spinler4a6ea6a2018-03-27 14:25:12 -0500164
Matt Spinler491fc6f2018-04-23 11:00:52 -0500165 addInterface(objectPath, InterfaceType::POLICY, anyObject);
Matt Spinler4a6ea6a2018-03-27 14:25:12 -0500166}
167#endif
168
Matt Spinler52ee71b2018-05-23 13:18:55 -0500169void 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
Matt Spinler60f53d82018-05-23 13:23:09 -0500240void 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
Matt Spinlere0017eb2018-03-27 11:17:38 -0500278void Manager::interfaceAdded(sdbusplus::message::message& msg)
279{
280 sdbusplus::message::object_path path;
281 DbusInterfaceMap interfaces;
282
283 msg.read(path, interfaces);
284
Matt Spinler259e7272018-03-29 10:57:17 -0500285 // Find the Logging.Entry interface with all of its properties
286 // to pass to create().
Matt Spinlere6a51592018-05-23 12:47:10 -0500287 if (interfaces.find(LOGGING_IFACE) != interfaces.end())
Matt Spinlere0017eb2018-03-27 11:17:38 -0500288 {
Matt Spinlere6a51592018-05-23 12:47:10 -0500289 create(path, interfaces);
Matt Spinlere0017eb2018-03-27 11:17:38 -0500290 }
291}
Matt Spinler055da3b2018-05-09 15:51:20 -0500292
Matt Spinler52ee71b2018-05-23 13:18:55 -0500293uint64_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
308fs::path Manager::getSaveDir(EntryID id)
309{
310 return fs::path{ERRLOG_PERSIST_PATH} / std::to_string(id);
311}
312
313fs::path Manager::getCalloutSaveDir(EntryID id)
314{
315 return getSaveDir(id) / "callouts";
316}
317
318std::string Manager::getCalloutObjectPath(const std::string& objectPath,
319 uint32_t calloutNum)
320{
321 return fs::path{objectPath} / "callouts" / std::to_string(calloutNum);
322}
323
Matt Spinler055da3b2018-05-09 15:51:20 -0500324void 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 {
Matt Spinlera1390352018-05-23 10:48:19 -0500338 erase(getEntryID(path));
Matt Spinler055da3b2018-05-09 15:51:20 -0500339 }
340}
Matt Spinlere0017eb2018-03-27 11:17:38 -0500341}
342}