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