1 /**
2 * Copyright © 2016 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 "manager.hpp"
17
18 #include "errors.hpp"
19
20 #include <algorithm>
21 #include <chrono>
22 #include <exception>
23 #include <filesystem>
24 #include <iostream>
25
26 using namespace std::literals::chrono_literals;
27
28 namespace phosphor
29 {
30 namespace inventory
31 {
32 namespace manager
33 {
34 /** @brief Fowrarding signal callback.
35 *
36 * Extracts per-signal specific context and forwards the call to the manager
37 * instance.
38 */
_signal(sd_bus_message * m,void * data,sd_bus_error *)39 auto _signal(sd_bus_message* m, void* data, sd_bus_error* /* e */) noexcept
40 {
41 try
42 {
43 auto msg = sdbusplus::message_t(m);
44 auto& args = *static_cast<Manager::SigArg*>(data);
45 sd_bus_message_ref(m);
46 auto& mgr = *std::get<0>(args);
47 mgr.handleEvent(msg, static_cast<const DbusSignal&>(*std::get<1>(args)),
48 *std::get<2>(args));
49 }
50 catch (const std::exception& e)
51 {
52 std::cerr << e.what() << std::endl;
53 }
54
55 return 0;
56 }
57
Manager(sdbusplus::bus_t && bus,const char * root)58 Manager::Manager(sdbusplus::bus_t&& bus, const char* root) :
59 ServerObject<ManagerIface>(bus, root), _root(root), _bus(std::move(bus)),
60 _manager(_bus, root),
61 #ifdef CREATE_ASSOCIATIONS
62 _associations(_bus),
63 #endif
64 _status(ManagerStatus::STARTING)
65 {
66 for (auto& group : _events)
67 {
68 for (auto pEvent : std::get<std::vector<EventBasePtr>>(group))
69 {
70 if (pEvent->type != Event::Type::DBUS_SIGNAL)
71 {
72 continue;
73 }
74
75 // Create a callback context for this event group.
76 auto dbusEvent = static_cast<DbusSignal*>(pEvent.get());
77
78 // Go ahead and store an iterator pointing at
79 // the event data to avoid lookups later since
80 // additional signal callbacks aren't added
81 // after the manager is constructed.
82 _sigargs.emplace_back(
83 std::make_unique<SigArg>(this, dbusEvent, &group));
84
85 // Register our callback and the context for
86 // each signal event.
87 _matches.emplace_back(_bus, dbusEvent->signature, _signal,
88 _sigargs.back().get());
89 }
90 }
91
92 // Restore any persistent inventory
93 restore();
94 }
95
shutdown()96 void Manager::shutdown() noexcept
97 {
98 _status = ManagerStatus::STOPPING;
99 }
100
run(const char * busname)101 void Manager::run(const char* busname)
102 {
103 sdbusplus::message_t unusedMsg{nullptr};
104
105 // Run startup events.
106 for (auto& group : _events)
107 {
108 for (auto pEvent : std::get<std::vector<EventBasePtr>>(group))
109 {
110 if (pEvent->type == Event::Type::STARTUP)
111 {
112 handleEvent(unusedMsg, *pEvent, group);
113 }
114 }
115 }
116
117 _status = ManagerStatus::RUNNING;
118 _bus.request_name(busname);
119
120 while (_status != ManagerStatus::STOPPING)
121 {
122 try
123 {
124 _bus.process_discard();
125 _bus.wait((5000000us).count());
126 }
127 catch (const std::exception& e)
128 {
129 std::cerr << e.what() << std::endl;
130 }
131 }
132 }
133
updateInterfaces(const sdbusplus::message::object_path & path,const Object & interfaces,ObjectReferences::iterator pos,bool newObject,bool restoreFromCache)134 void Manager::updateInterfaces(const sdbusplus::message::object_path& path,
135 const Object& interfaces,
136 ObjectReferences::iterator pos, bool newObject,
137 bool restoreFromCache)
138 {
139 auto& refaces = pos->second;
140 auto ifaceit = interfaces.cbegin();
141 auto opsit = _makers.cbegin();
142 auto refaceit = refaces.begin();
143 std::vector<std::string> signals;
144
145 while (ifaceit != interfaces.cend())
146 {
147 try
148 {
149 // Find the binding ops for this interface.
150 opsit = std::lower_bound(opsit, _makers.cend(), ifaceit->first,
151 compareFirst(_makers.key_comp()));
152
153 if (opsit == _makers.cend() || opsit->first != ifaceit->first)
154 {
155 // This interface is not supported.
156 throw InterfaceError("Encountered unsupported interface.",
157 ifaceit->first);
158 }
159
160 // Find the binding insertion point or the binding to update.
161 refaceit = std::lower_bound(refaceit, refaces.end(), ifaceit->first,
162 compareFirst(refaces.key_comp()));
163
164 if (refaceit == refaces.end() || refaceit->first != ifaceit->first)
165 {
166 // Add the new interface.
167 auto& ctor = std::get<MakeInterfaceType>(opsit->second);
168 // skipSignal = true here to avoid getting PropertiesChanged
169 // signals while the interface is constructed. We'll emit an
170 // ObjectManager signal for this interface below.
171 refaceit = refaces.insert(
172 refaceit, std::make_pair(ifaceit->first,
173 ctor(_bus, path.str.c_str(),
174 ifaceit->second, true)));
175 signals.push_back(ifaceit->first);
176 }
177 else
178 {
179 // Set the new property values.
180 auto& assign = std::get<AssignInterfaceType>(opsit->second);
181 assign(ifaceit->second, refaceit->second,
182 _status != ManagerStatus::RUNNING);
183 }
184 if (!restoreFromCache)
185 {
186 auto& serialize =
187 std::get<SerializeInterfaceType<SerialOps>>(opsit->second);
188 serialize(path, ifaceit->first, refaceit->second);
189 }
190 else
191 {
192 auto& deserialize =
193 std::get<DeserializeInterfaceType<SerialOps>>(
194 opsit->second);
195 deserialize(path, ifaceit->first, refaceit->second);
196 }
197 }
198 catch (const InterfaceError& e)
199 {
200 // Reset the binding ops iterator since we are
201 // at the end.
202 opsit = _makers.cbegin();
203 e.log();
204 }
205
206 ++ifaceit;
207 }
208
209 if (_status == ManagerStatus::RUNNING)
210 {
211 if (newObject)
212 {
213 _bus.emit_object_added(path.str.c_str());
214 }
215 else if (!signals.empty())
216 {
217 _bus.emit_interfaces_added(path.str.c_str(), signals);
218 }
219 }
220 }
221
updateObjects(const std::map<sdbusplus::message::object_path,Object> & objs,bool restoreFromCache)222 void Manager::updateObjects(
223 const std::map<sdbusplus::message::object_path, Object>& objs,
224 bool restoreFromCache)
225 {
226 auto objit = objs.cbegin();
227 auto refit = _refs.begin();
228 std::string absPath;
229 bool newObj;
230
231 while (objit != objs.cend())
232 {
233 // Find the insertion point or the object to update.
234 refit = std::lower_bound(refit, _refs.end(), objit->first,
235 compareFirst(RelPathCompare(_root)));
236
237 absPath.assign(_root);
238 absPath.append(objit->first);
239
240 newObj = false;
241 if (refit == _refs.end() || refit->first != absPath)
242 {
243 refit = _refs.insert(
244 refit, std::make_pair(absPath, decltype(_refs)::mapped_type()));
245 newObj = true;
246 }
247
248 updateInterfaces(absPath, objit->second, refit, newObj,
249 restoreFromCache);
250 #ifdef CREATE_ASSOCIATIONS
251 if (!_associations.pendingCondition() && newObj)
252 {
253 _associations.createAssociations(absPath,
254 _status != ManagerStatus::RUNNING);
255 }
256 else if (!restoreFromCache &&
257 _associations.conditionMatch(objit->first, objit->second))
258 {
259 // The objit path/interface/property matched a pending condition.
260 // Now the associations are valid so attempt to create them against
261 // all existing objects. If this was the restoreFromCache path,
262 // objit doesn't contain property values so don't bother checking.
263 std::for_each(_refs.begin(), _refs.end(), [this](const auto& ref) {
264 _associations.createAssociations(
265 ref.first, _status != ManagerStatus::RUNNING);
266 });
267 }
268 #endif
269 ++objit;
270 }
271 }
272
notify(std::map<sdbusplus::message::object_path,Object> objs)273 void Manager::notify(std::map<sdbusplus::message::object_path, Object> objs)
274 {
275 updateObjects(objs);
276 }
277
handleEvent(sdbusplus::message_t & msg,const Event & event,const EventInfo & info)278 void Manager::handleEvent(sdbusplus::message_t& msg, const Event& event,
279 const EventInfo& info)
280 {
281 auto& actions = std::get<1>(info);
282
283 for (auto& f : event)
284 {
285 if (!f(_bus, msg, *this))
286 {
287 return;
288 }
289 }
290 for (auto& action : actions)
291 {
292 action(_bus, *this);
293 }
294 }
295
destroyObjects(const std::vector<const char * > & paths)296 void Manager::destroyObjects(const std::vector<const char*>& paths)
297 {
298 std::string p;
299
300 for (const auto& path : paths)
301 {
302 p.assign(_root);
303 p.append(path);
304 _bus.emit_object_removed(p.c_str());
305 _refs.erase(p);
306 }
307 }
308
createObjects(const std::map<sdbusplus::message::object_path,Object> & objs)309 void Manager::createObjects(
310 const std::map<sdbusplus::message::object_path, Object>& objs)
311 {
312 updateObjects(objs);
313 }
314
getInterfaceHolder(const char * path,const char * interface)315 std::any& Manager::getInterfaceHolder(const char* path, const char* interface)
316 {
317 return const_cast<std::any&>(
318 const_cast<const Manager*>(this)->getInterfaceHolder(path, interface));
319 }
320
getInterfaceHolder(const char * path,const char * interface) const321 const std::any& Manager::getInterfaceHolder(const char* path,
322 const char* interface) const
323 {
324 std::string p{path};
325 auto oit = _refs.find(_root + p);
326 if (oit == _refs.end())
327 throw std::runtime_error(_root + p + " was not found");
328
329 auto& obj = oit->second;
330 auto iit = obj.find(interface);
331 if (iit == obj.end())
332 throw std::runtime_error("interface was not found");
333
334 return iit->second;
335 }
336
restore()337 void Manager::restore()
338 {
339 namespace fs = std::filesystem;
340
341 if (!fs::exists(fs::path(PIM_PERSIST_PATH)))
342 {
343 return;
344 }
345
346 static const std::string remove = std::string(PIM_PERSIST_PATH) +
347 INVENTORY_ROOT;
348
349 std::map<sdbusplus::message::object_path, Object> objects;
350 for (const auto& dirent :
351 fs::recursive_directory_iterator(PIM_PERSIST_PATH))
352 {
353 const auto& path = dirent.path();
354 if (fs::is_regular_file(path))
355 {
356 auto ifaceName = path.filename().string();
357 auto objPath = path.parent_path().string();
358 objPath.erase(0, remove.length());
359 auto objit = objects.find(objPath);
360 Interface propertyMap{};
361 if (objects.end() != objit)
362 {
363 auto& object = objit->second;
364 object.emplace(std::move(ifaceName), std::move(propertyMap));
365 }
366 else
367 {
368 Object object;
369 object.emplace(std::move(ifaceName), std::move(propertyMap));
370 objects.emplace(std::move(objPath), std::move(object));
371 }
372 }
373 }
374 if (!objects.empty())
375 {
376 auto restoreFromCache = true;
377 updateObjects(objects, restoreFromCache);
378
379 #ifdef CREATE_ASSOCIATIONS
380 // There may be conditional associations waiting to be loaded
381 // based on certain path/interface/property values. Now that
382 // _refs contains all objects with their property values, check
383 // which property values the conditions need and set them in the
384 // condition structure entries, using the actualValue field. Then
385 // the associations manager can check if the conditions are met.
386 if (_associations.pendingCondition())
387 {
388 ObjectReferences::iterator refIt;
389 InterfaceComposite::iterator ifaceIt;
390
391 auto& conditions = _associations.getConditions();
392 for (auto& condition : conditions)
393 {
394 refIt = _refs.find(_root + condition.path);
395 if (refIt != _refs.end())
396 {
397 ifaceIt = refIt->second.find(condition.interface);
398 }
399
400 if ((refIt != _refs.end()) && (ifaceIt != refIt->second.end()))
401 {
402 const auto& maker = _makers.find(condition.interface);
403 if (maker != _makers.end())
404 {
405 auto& getProperty =
406 std::get<GetPropertyValueType>(maker->second);
407
408 condition.actualValue = getProperty(condition.property,
409 ifaceIt->second);
410 }
411 }
412 }
413
414 // Check if a property value in a condition matches an
415 // actual property value just saved. If one did, now the
416 // associations file is valid so create its associations.
417 if (_associations.conditionMatch())
418 {
419 std::for_each(_refs.begin(), _refs.end(),
420 [this](const auto& ref) {
421 _associations.createAssociations(
422 ref.first, _status != ManagerStatus::RUNNING);
423 });
424 }
425 }
426 #endif
427 }
428 }
429
430 } // namespace manager
431 } // namespace inventory
432 } // namespace phosphor
433
434 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
435