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