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(
135 const sdbusplus::message::object_path& path, const Object& interfaces,
136 ObjectReferences::iterator pos, bool newObject, bool restoreFromCache)
137 {
138 auto& refaces = pos->second;
139 auto ifaceit = interfaces.cbegin();
140 auto opsit = _makers.cbegin();
141 auto refaceit = refaces.begin();
142 std::vector<std::string> signals;
143
144 while (ifaceit != interfaces.cend())
145 {
146 try
147 {
148 // Find the binding ops for this interface.
149 opsit = std::lower_bound(opsit, _makers.cend(), ifaceit->first,
150 compareFirst(_makers.key_comp()));
151
152 if (opsit == _makers.cend() || opsit->first != ifaceit->first)
153 {
154 // This interface is not supported.
155 throw InterfaceError("Encountered unsupported interface.",
156 ifaceit->first);
157 }
158
159 // Find the binding insertion point or the binding to update.
160 refaceit = std::lower_bound(refaceit, refaces.end(), ifaceit->first,
161 compareFirst(refaces.key_comp()));
162
163 if (refaceit == refaces.end() || refaceit->first != ifaceit->first)
164 {
165 // Add the new interface.
166 auto& ctor = std::get<MakeInterfaceType>(opsit->second);
167 // skipSignal = true here to avoid getting PropertiesChanged
168 // signals while the interface is constructed. We'll emit an
169 // ObjectManager signal for this interface below.
170 refaceit = refaces.insert(
171 refaceit, std::make_pair(ifaceit->first,
172 ctor(_bus, path.str.c_str(),
173 ifaceit->second, true)));
174 signals.push_back(ifaceit->first);
175 }
176 else
177 {
178 // Set the new property values.
179 auto& assign = std::get<AssignInterfaceType>(opsit->second);
180 assign(ifaceit->second, refaceit->second,
181 _status != ManagerStatus::RUNNING);
182 }
183 if (!restoreFromCache)
184 {
185 auto& serialize =
186 std::get<SerializeInterfaceType<SerialOps>>(opsit->second);
187 serialize(path, ifaceit->first, refaceit->second);
188 }
189 else
190 {
191 auto& deserialize =
192 std::get<DeserializeInterfaceType<SerialOps>>(
193 opsit->second);
194 deserialize(path, ifaceit->first, refaceit->second);
195 }
196 }
197 catch (const InterfaceError& e)
198 {
199 // Reset the binding ops iterator since we are
200 // at the end.
201 opsit = _makers.cbegin();
202 e.log();
203 }
204
205 ++ifaceit;
206 }
207
208 if (_status == ManagerStatus::RUNNING)
209 {
210 if (newObject)
211 {
212 _bus.emit_object_added(path.str.c_str());
213 }
214 else if (!signals.empty())
215 {
216 _bus.emit_interfaces_added(path.str.c_str(), signals);
217 }
218 }
219 }
220
updateObjects(const std::map<sdbusplus::message::object_path,Object> & objs,bool restoreFromCache)221 void Manager::updateObjects(
222 const std::map<sdbusplus::message::object_path, Object>& objs,
223 bool restoreFromCache)
224 {
225 auto objit = objs.cbegin();
226 auto refit = _refs.begin();
227 std::string absPath;
228 bool newObj;
229
230 while (objit != objs.cend())
231 {
232 // Find the insertion point or the object to update.
233 refit = std::lower_bound(refit, _refs.end(), objit->first,
234 compareFirst(RelPathCompare(_root)));
235
236 absPath.assign(_root);
237 absPath.append(objit->first);
238
239 newObj = false;
240 if (refit == _refs.end() || refit->first != absPath)
241 {
242 refit = _refs.insert(
243 refit, std::make_pair(absPath, decltype(_refs)::mapped_type()));
244 newObj = true;
245 }
246
247 updateInterfaces(absPath, objit->second, refit, newObj,
248 restoreFromCache);
249 #ifdef CREATE_ASSOCIATIONS
250 if (!_associations.pendingCondition() && newObj)
251 {
252 _associations.createAssociations(absPath,
253 _status != ManagerStatus::RUNNING);
254 }
255 else if (!restoreFromCache &&
256 _associations.conditionMatch(objit->first, objit->second))
257 {
258 // The objit path/interface/property matched a pending condition.
259 // Now the associations are valid so attempt to create them against
260 // all existing objects. If this was the restoreFromCache path,
261 // objit doesn't contain property values so don't bother checking.
262 std::for_each(_refs.begin(), _refs.end(), [this](const auto& ref) {
263 _associations.createAssociations(
264 ref.first, _status != ManagerStatus::RUNNING);
265 });
266 }
267 #endif
268 ++objit;
269 }
270 }
271
notify(std::map<sdbusplus::message::object_path,Object> objs)272 void Manager::notify(std::map<sdbusplus::message::object_path, Object> objs)
273 {
274 updateObjects(objs);
275 }
276
handleEvent(sdbusplus::message_t & msg,const Event & event,const EventInfo & info)277 void Manager::handleEvent(sdbusplus::message_t& msg, const Event& event,
278 const EventInfo& info)
279 {
280 auto& actions = std::get<1>(info);
281
282 for (auto& f : event)
283 {
284 if (!f(_bus, msg, *this))
285 {
286 return;
287 }
288 }
289 for (auto& action : actions)
290 {
291 action(_bus, *this);
292 }
293 }
294
destroyObjects(const std::vector<const char * > & paths)295 void Manager::destroyObjects(const std::vector<const char*>& paths)
296 {
297 std::string p;
298
299 for (const auto& path : paths)
300 {
301 p.assign(_root);
302 p.append(path);
303 _bus.emit_object_removed(p.c_str());
304 _refs.erase(p);
305 }
306 }
307
createObjects(const std::map<sdbusplus::message::object_path,Object> & objs)308 void Manager::createObjects(
309 const std::map<sdbusplus::message::object_path, Object>& objs)
310 {
311 updateObjects(objs);
312 }
313
getInterfaceHolder(const char * path,const char * interface)314 std::any& Manager::getInterfaceHolder(const char* path, const char* interface)
315 {
316 return const_cast<std::any&>(
317 const_cast<const Manager*>(this)->getInterfaceHolder(path, interface));
318 }
319
320 const std::any&
getInterfaceHolder(const char * path,const char * interface) const321 Manager::getInterfaceHolder(const char* path, const char* interface) const
322 {
323 std::string p{path};
324 auto oit = _refs.find(_root + p);
325 if (oit == _refs.end())
326 throw std::runtime_error(_root + p + " was not found");
327
328 auto& obj = oit->second;
329 auto iit = obj.find(interface);
330 if (iit == obj.end())
331 throw std::runtime_error("interface was not found");
332
333 return iit->second;
334 }
335
restore()336 void Manager::restore()
337 {
338 namespace fs = std::filesystem;
339
340 if (!fs::exists(fs::path(PIM_PERSIST_PATH)))
341 {
342 return;
343 }
344
345 static const std::string remove =
346 std::string(PIM_PERSIST_PATH) + INVENTORY_ROOT;
347
348 std::map<sdbusplus::message::object_path, Object> objects;
349 for (const auto& dirent :
350 fs::recursive_directory_iterator(PIM_PERSIST_PATH))
351 {
352 const auto& path = dirent.path();
353 if (fs::is_regular_file(path))
354 {
355 auto ifaceName = path.filename().string();
356 auto objPath = path.parent_path().string();
357 objPath.erase(0, remove.length());
358 auto objit = objects.find(objPath);
359 Interface propertyMap{};
360 if (objects.end() != objit)
361 {
362 auto& object = objit->second;
363 object.emplace(std::move(ifaceName), std::move(propertyMap));
364 }
365 else
366 {
367 Object object;
368 object.emplace(std::move(ifaceName), std::move(propertyMap));
369 objects.emplace(std::move(objPath), std::move(object));
370 }
371 }
372 }
373 if (!objects.empty())
374 {
375 auto restoreFromCache = true;
376 updateObjects(objects, restoreFromCache);
377
378 #ifdef CREATE_ASSOCIATIONS
379 // There may be conditional associations waiting to be loaded
380 // based on certain path/interface/property values. Now that
381 // _refs contains all objects with their property values, check
382 // which property values the conditions need and set them in the
383 // condition structure entries, using the actualValue field. Then
384 // the associations manager can check if the conditions are met.
385 if (_associations.pendingCondition())
386 {
387 ObjectReferences::iterator refIt;
388 InterfaceComposite::iterator ifaceIt;
389
390 auto& conditions = _associations.getConditions();
391 for (auto& condition : conditions)
392 {
393 refIt = _refs.find(_root + condition.path);
394 if (refIt != _refs.end())
395 {
396 ifaceIt = refIt->second.find(condition.interface);
397 }
398
399 if ((refIt != _refs.end()) && (ifaceIt != refIt->second.end()))
400 {
401 const auto& maker = _makers.find(condition.interface);
402 if (maker != _makers.end())
403 {
404 auto& getProperty =
405 std::get<GetPropertyValueType>(maker->second);
406
407 condition.actualValue =
408 getProperty(condition.property, ifaceIt->second);
409 }
410 }
411 }
412
413 // Check if a property value in a condition matches an
414 // actual property value just saved. If one did, now the
415 // associations file is valid so create its associations.
416 if (_associations.conditionMatch())
417 {
418 std::for_each(
419 _refs.begin(), _refs.end(), [this](const auto& ref) {
420 _associations.createAssociations(
421 ref.first, _status != ManagerStatus::RUNNING);
422 });
423 }
424 }
425 #endif
426 }
427 }
428
429 } // namespace manager
430 } // namespace inventory
431 } // namespace phosphor
432
433 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
434