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), _shutdown(false), _root(root),
62     _bus(std::move(bus)), _manager(_bus, root)
63 #ifdef CREATE_ASSOCIATIONS
64     ,
65     _associations(_bus)
66 #endif
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     _shutdown = true;
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     _bus.request_name(busname);
120     while (!_shutdown)
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 
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                 refaceit = refaces.insert(
169                     refaceit,
170                     std::make_pair(ifaceit->first, ctor(_bus, path.str.c_str(),
171                                                         ifaceit->second)));
172                 signals.push_back(ifaceit->first);
173             }
174             else
175             {
176                 // Set the new property values.
177                 auto& assign = std::get<AssignInterfaceType>(opsit->second);
178                 assign(ifaceit->second, refaceit->second);
179             }
180             if (!restoreFromCache)
181             {
182                 auto& serialize =
183                     std::get<SerializeInterfaceType<SerialOps>>(opsit->second);
184                 serialize(path, ifaceit->first, refaceit->second);
185             }
186             else
187             {
188                 auto& deserialize =
189                     std::get<DeserializeInterfaceType<SerialOps>>(
190                         opsit->second);
191                 deserialize(path, ifaceit->first, refaceit->second);
192             }
193         }
194         catch (const InterfaceError& e)
195         {
196             // Reset the binding ops iterator since we are
197             // at the end.
198             opsit = _makers.cbegin();
199             e.log();
200         }
201 
202         ++ifaceit;
203     }
204 
205     if (newObject)
206     {
207         _bus.emit_object_added(path.str.c_str());
208     }
209     else if (!signals.empty())
210     {
211         _bus.emit_interfaces_added(path.str.c_str(), signals);
212     }
213 }
214 
215 void Manager::updateObjects(
216     const std::map<sdbusplus::message::object_path, Object>& objs,
217     bool restoreFromCache)
218 {
219     auto objit = objs.cbegin();
220     auto refit = _refs.begin();
221     std::string absPath;
222     bool newObj;
223 
224     while (objit != objs.cend())
225     {
226         // Find the insertion point or the object to update.
227         refit = std::lower_bound(refit, _refs.end(), objit->first,
228                                  compareFirst(RelPathCompare(_root)));
229 
230         absPath.assign(_root);
231         absPath.append(objit->first);
232 
233         newObj = false;
234         if (refit == _refs.end() || refit->first != absPath)
235         {
236             refit = _refs.insert(
237                 refit, std::make_pair(absPath, decltype(_refs)::mapped_type()));
238             newObj = true;
239         }
240 
241         updateInterfaces(absPath, objit->second, refit, newObj,
242                          restoreFromCache);
243 #ifdef CREATE_ASSOCIATIONS
244         if (newObj)
245         {
246             _associations.createAssociations(absPath);
247         }
248 #endif
249         ++objit;
250     }
251 }
252 
253 void Manager::notify(std::map<sdbusplus::message::object_path, Object> objs)
254 {
255     updateObjects(objs);
256 }
257 
258 void Manager::handleEvent(sdbusplus::message::message& msg, const Event& event,
259                           const EventInfo& info)
260 {
261     auto& actions = std::get<1>(info);
262 
263     for (auto& f : event)
264     {
265         if (!f(_bus, msg, *this))
266         {
267             return;
268         }
269     }
270     for (auto& action : actions)
271     {
272         action(_bus, *this);
273     }
274 }
275 
276 void Manager::destroyObjects(const std::vector<const char*>& paths)
277 {
278     std::string p;
279 
280     for (const auto& path : paths)
281     {
282         p.assign(_root);
283         p.append(path);
284         _bus.emit_object_removed(p.c_str());
285         _refs.erase(p);
286     }
287 }
288 
289 void Manager::createObjects(
290     const std::map<sdbusplus::message::object_path, Object>& objs)
291 {
292     updateObjects(objs);
293 }
294 
295 std::any& Manager::getInterfaceHolder(const char* path, const char* interface)
296 {
297     return const_cast<std::any&>(
298         const_cast<const Manager*>(this)->getInterfaceHolder(path, interface));
299 }
300 
301 const std::any& Manager::getInterfaceHolder(const char* path,
302                                             const char* interface) const
303 {
304     std::string p{path};
305     auto oit = _refs.find(_root + p);
306     if (oit == _refs.end())
307         throw std::runtime_error(_root + p + " was not found");
308 
309     auto& obj = oit->second;
310     auto iit = obj.find(interface);
311     if (iit == obj.end())
312         throw std::runtime_error("interface was not found");
313 
314     return iit->second;
315 }
316 
317 void Manager::restore()
318 {
319     namespace fs = std::filesystem;
320 
321     if (!fs::exists(fs::path(PIM_PERSIST_PATH)))
322     {
323         return;
324     }
325 
326     static const std::string remove =
327         std::string(PIM_PERSIST_PATH) + INVENTORY_ROOT;
328 
329     std::map<sdbusplus::message::object_path, Object> objects;
330     for (const auto& dirent :
331          fs::recursive_directory_iterator(PIM_PERSIST_PATH))
332     {
333         const auto& path = dirent.path();
334         if (fs::is_regular_file(path))
335         {
336             auto ifaceName = path.filename().string();
337             auto objPath = path.parent_path().string();
338             objPath.erase(0, remove.length());
339             auto objit = objects.find(objPath);
340             Interface propertyMap{};
341             if (objects.end() != objit)
342             {
343                 auto& object = objit->second;
344                 object.emplace(std::move(ifaceName), std::move(propertyMap));
345             }
346             else
347             {
348                 Object object;
349                 object.emplace(std::move(ifaceName), std::move(propertyMap));
350                 objects.emplace(std::move(objPath), std::move(object));
351             }
352         }
353     }
354     if (!objects.empty())
355     {
356         auto restoreFromCache = true;
357         updateObjects(objects, restoreFromCache);
358     }
359 }
360 
361 } // namespace manager
362 } // namespace inventory
363 } // namespace phosphor
364 
365 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
366