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