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* busname,
61                  const char* root) :
62     ServerObject<ManagerIface>(bus, root),
63     _shutdown(false), _root(root), _bus(std::move(bus)), _manager(_bus, root)
64 #ifdef CREATE_ASSOCIATIONS
65     ,
66     _associations(_bus)
67 #endif
68 {
69     for (auto& group : _events)
70     {
71         for (auto pEvent : std::get<std::vector<EventBasePtr>>(group))
72         {
73             if (pEvent->type != Event::Type::DBUS_SIGNAL)
74             {
75                 continue;
76             }
77 
78             // Create a callback context for this event group.
79             auto dbusEvent = static_cast<DbusSignal*>(pEvent.get());
80 
81             // Go ahead and store an iterator pointing at
82             // the event data to avoid lookups later since
83             // additional signal callbacks aren't added
84             // after the manager is constructed.
85             _sigargs.emplace_back(
86                 std::make_unique<SigArg>(this, dbusEvent, &group));
87 
88             // Register our callback and the context for
89             // each signal event.
90             _matches.emplace_back(_bus, dbusEvent->signature, _signal,
91                                   _sigargs.back().get());
92         }
93     }
94 
95     // Restore any persistent inventory
96     restore();
97 
98     _bus.request_name(busname);
99 }
100 
101 void Manager::shutdown() noexcept
102 {
103     _shutdown = true;
104 }
105 
106 void Manager::run() noexcept
107 {
108     sdbusplus::message::message unusedMsg{nullptr};
109 
110     // Run startup events.
111     for (auto& group : _events)
112     {
113         for (auto pEvent : std::get<std::vector<EventBasePtr>>(group))
114         {
115             if (pEvent->type == Event::Type::STARTUP)
116             {
117                 handleEvent(unusedMsg, *pEvent, group);
118             }
119         }
120     }
121 
122     while (!_shutdown)
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                 refaceit = refaces.insert(
171                     refaceit,
172                     std::make_pair(ifaceit->first, ctor(_bus, path.str.c_str(),
173                                                         ifaceit->second)));
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             }
182             if (!restoreFromCache)
183             {
184                 auto& serialize =
185                     std::get<SerializeInterfaceType<SerialOps>>(opsit->second);
186                 serialize(path, ifaceit->first, refaceit->second);
187             }
188             else
189             {
190                 auto& deserialize =
191                     std::get<DeserializeInterfaceType<SerialOps>>(
192                         opsit->second);
193                 deserialize(path, ifaceit->first, refaceit->second);
194             }
195         }
196         catch (const InterfaceError& e)
197         {
198             // Reset the binding ops iterator since we are
199             // at the end.
200             opsit = _makers.cbegin();
201             e.log();
202         }
203 
204         ++ifaceit;
205     }
206 
207     if (newObject)
208     {
209         _bus.emit_object_added(path.str.c_str());
210     }
211     else if (!signals.empty())
212     {
213         _bus.emit_interfaces_added(path.str.c_str(), signals);
214     }
215 }
216 
217 void Manager::updateObjects(
218     const std::map<sdbusplus::message::object_path, Object>& objs,
219     bool restoreFromCache)
220 {
221     auto objit = objs.cbegin();
222     auto refit = _refs.begin();
223     std::string absPath;
224     bool newObj;
225 
226     while (objit != objs.cend())
227     {
228         // Find the insertion point or the object to update.
229         refit = std::lower_bound(refit, _refs.end(), objit->first,
230                                  compareFirst(RelPathCompare(_root)));
231 
232         absPath.assign(_root);
233         absPath.append(objit->first);
234 
235         newObj = false;
236         if (refit == _refs.end() || refit->first != absPath)
237         {
238             refit = _refs.insert(
239                 refit, std::make_pair(absPath, decltype(_refs)::mapped_type()));
240             newObj = true;
241         }
242 
243         updateInterfaces(absPath, objit->second, refit, newObj,
244                          restoreFromCache);
245 #ifdef CREATE_ASSOCIATIONS
246         if (newObj)
247         {
248             _associations.createAssociations(absPath);
249         }
250 #endif
251         ++objit;
252     }
253 }
254 
255 void Manager::notify(std::map<sdbusplus::message::object_path, Object> objs)
256 {
257     updateObjects(objs);
258 }
259 
260 void Manager::handleEvent(sdbusplus::message::message& msg, const Event& event,
261                           const EventInfo& info)
262 {
263     auto& actions = std::get<1>(info);
264 
265     for (auto& f : event)
266     {
267         if (!f(_bus, msg, *this))
268         {
269             return;
270         }
271     }
272     for (auto& action : actions)
273     {
274         action(_bus, *this);
275     }
276 }
277 
278 void Manager::destroyObjects(const std::vector<const char*>& paths)
279 {
280     std::string p;
281 
282     for (const auto& path : paths)
283     {
284         p.assign(_root);
285         p.append(path);
286         _bus.emit_object_removed(p.c_str());
287         _refs.erase(p);
288     }
289 }
290 
291 void Manager::createObjects(
292     const std::map<sdbusplus::message::object_path, Object>& objs)
293 {
294     updateObjects(objs);
295 }
296 
297 std::any& Manager::getInterfaceHolder(const char* path, const char* interface)
298 {
299     return const_cast<std::any&>(
300         const_cast<const Manager*>(this)->getInterfaceHolder(path, interface));
301 }
302 
303 const std::any& Manager::getInterfaceHolder(const char* path,
304                                             const char* interface) const
305 {
306     std::string p{path};
307     auto oit = _refs.find(_root + p);
308     if (oit == _refs.end())
309         throw std::runtime_error(_root + p + " was not found");
310 
311     auto& obj = oit->second;
312     auto iit = obj.find(interface);
313     if (iit == obj.end())
314         throw std::runtime_error("interface was not found");
315 
316     return iit->second;
317 }
318 
319 void Manager::restore()
320 {
321     namespace fs = std::filesystem;
322 
323     if (!fs::exists(fs::path(PIM_PERSIST_PATH)))
324     {
325         return;
326     }
327 
328     static const std::string remove =
329         std::string(PIM_PERSIST_PATH) + INVENTORY_ROOT;
330 
331     std::map<sdbusplus::message::object_path, Object> objects;
332     for (const auto& dirent :
333          fs::recursive_directory_iterator(PIM_PERSIST_PATH))
334     {
335         const auto& path = dirent.path();
336         if (fs::is_regular_file(path))
337         {
338             auto ifaceName = path.filename().string();
339             auto objPath = path.parent_path().string();
340             objPath.erase(0, remove.length());
341             auto objit = objects.find(objPath);
342             Interface propertyMap{};
343             if (objects.end() != objit)
344             {
345                 auto& object = objit->second;
346                 object.emplace(std::move(ifaceName), std::move(propertyMap));
347             }
348             else
349             {
350                 Object object;
351                 object.emplace(std::move(ifaceName), std::move(propertyMap));
352                 objects.emplace(std::move(objPath), std::move(object));
353             }
354         }
355     }
356     if (!objects.empty())
357     {
358         auto restoreFromCache = true;
359         updateObjects(objects, restoreFromCache);
360     }
361 }
362 
363 } // namespace manager
364 } // namespace inventory
365 } // namespace phosphor
366 
367 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
368