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 #include <phosphor-logging/log.hpp>
26 
27 using namespace std::literals::chrono_literals;
28 
29 namespace phosphor
30 {
31 namespace inventory
32 {
33 namespace manager
34 {
35 /** @brief Fowrarding signal callback.
36  *
37  *  Extracts per-signal specific context and forwards the call to the manager
38  *  instance.
39  */
40 auto _signal(sd_bus_message* m, void* data, sd_bus_error* e) noexcept
41 {
42     try
43     {
44         auto msg = sdbusplus::message::message(m);
45         auto& args = *static_cast<Manager::SigArg*>(data);
46         sd_bus_message_ref(m);
47         auto& mgr = *std::get<0>(args);
48         mgr.handleEvent(msg, static_cast<const DbusSignal&>(*std::get<1>(args)),
49                         *std::get<2>(args));
50     }
51     catch (const std::exception& e)
52     {
53         std::cerr << e.what() << std::endl;
54     }
55 
56     return 0;
57 }
58 
59 Manager::Manager(sdbusplus::bus::bus&& bus, const char* busname,
60                  const char* root, const char* iface) :
61     ServerObject<ManagerIface>(bus, root),
62     _shutdown(false), _root(root), _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     _bus.request_name(busname);
95 
96     // Restore any persistent inventory
97     restore();
98 }
99 
100 void Manager::shutdown() noexcept
101 {
102     _shutdown = true;
103 }
104 
105 void Manager::run() noexcept
106 {
107     sdbusplus::message::message unusedMsg{nullptr};
108 
109     // Run startup events.
110     for (auto& group : _events)
111     {
112         for (auto pEvent : std::get<std::vector<EventBasePtr>>(group))
113         {
114             if (pEvent->type == Event::Type::STARTUP)
115             {
116                 handleEvent(unusedMsg, *pEvent, group);
117             }
118         }
119     }
120 
121     while (!_shutdown)
122     {
123         try
124         {
125             _bus.process_discard();
126             _bus.wait((5000000us).count());
127         }
128         catch (const std::exception& e)
129         {
130             std::cerr << e.what() << std::endl;
131         }
132     }
133 }
134 
135 void Manager::updateInterfaces(const sdbusplus::message::object_path& path,
136                                const Object& interfaces,
137                                ObjectReferences::iterator pos, bool newObject,
138                                bool restoreFromCache)
139 {
140     auto& refaces = pos->second;
141     auto ifaceit = interfaces.cbegin();
142     auto opsit = _makers.cbegin();
143     auto refaceit = refaces.begin();
144     std::vector<std::string> signals;
145 
146     while (ifaceit != interfaces.cend())
147     {
148         try
149         {
150             // Find the binding ops for this interface.
151             opsit = std::lower_bound(opsit, _makers.cend(), ifaceit->first,
152                                      compareFirst(_makers.key_comp()));
153 
154             if (opsit == _makers.cend() || opsit->first != ifaceit->first)
155             {
156                 // This interface is not supported.
157                 throw InterfaceError("Encountered unsupported interface.",
158                                      ifaceit->first);
159             }
160 
161             // Find the binding insertion point or the binding to update.
162             refaceit = std::lower_bound(refaceit, refaces.end(), ifaceit->first,
163                                         compareFirst(refaces.key_comp()));
164 
165             if (refaceit == refaces.end() || refaceit->first != ifaceit->first)
166             {
167                 // Add the new interface.
168                 auto& ctor = std::get<MakeInterfaceType>(opsit->second);
169                 refaceit = refaces.insert(
170                     refaceit,
171                     std::make_pair(ifaceit->first, ctor(_bus, path.str.c_str(),
172                                                         ifaceit->second)));
173                 signals.push_back(ifaceit->first);
174             }
175             else
176             {
177                 // Set the new property values.
178                 auto& assign = std::get<AssignInterfaceType>(opsit->second);
179                 assign(ifaceit->second, refaceit->second);
180             }
181             if (!restoreFromCache)
182             {
183                 auto& serialize =
184                     std::get<SerializeInterfaceType<SerialOps>>(opsit->second);
185                 serialize(path, ifaceit->first, refaceit->second);
186             }
187             else
188             {
189                 auto& deserialize =
190                     std::get<DeserializeInterfaceType<SerialOps>>(
191                         opsit->second);
192                 deserialize(path, ifaceit->first, refaceit->second);
193             }
194         }
195         catch (const InterfaceError& e)
196         {
197             // Reset the binding ops iterator since we are
198             // at the end.
199             opsit = _makers.cbegin();
200             e.log();
201         }
202 
203         ++ifaceit;
204     }
205 
206     if (newObject)
207     {
208         _bus.emit_object_added(path.str.c_str());
209     }
210     else if (!signals.empty())
211     {
212         _bus.emit_interfaces_added(path.str.c_str(), signals);
213     }
214 }
215 
216 void Manager::updateObjects(
217     const std::map<sdbusplus::message::object_path, Object>& objs,
218     bool restoreFromCache)
219 {
220     auto objit = objs.cbegin();
221     auto refit = _refs.begin();
222     std::string absPath;
223     bool newObj;
224 
225     while (objit != objs.cend())
226     {
227         // Find the insertion point or the object to update.
228         refit = std::lower_bound(refit, _refs.end(), objit->first,
229                                  compareFirst(RelPathCompare(_root)));
230 
231         absPath.assign(_root);
232         absPath.append(objit->first);
233 
234         newObj = false;
235         if (refit == _refs.end() || refit->first != absPath)
236         {
237             refit = _refs.insert(
238                 refit, std::make_pair(absPath, decltype(_refs)::mapped_type()));
239             newObj = true;
240         }
241 
242         updateInterfaces(absPath, objit->second, refit, newObj,
243                          restoreFromCache);
244 #ifdef CREATE_ASSOCIATIONS
245         if (newObj)
246         {
247             _associations.createAssociations(absPath);
248         }
249 #endif
250         ++objit;
251     }
252 }
253 
254 void Manager::notify(std::map<sdbusplus::message::object_path, Object> objs)
255 {
256     updateObjects(objs);
257 }
258 
259 void Manager::handleEvent(sdbusplus::message::message& msg, const Event& event,
260                           const EventInfo& info)
261 {
262     auto& actions = std::get<1>(info);
263 
264     for (auto& f : event)
265     {
266         if (!f(_bus, msg, *this))
267         {
268             return;
269         }
270     }
271     for (auto& action : actions)
272     {
273         action(_bus, *this);
274     }
275 }
276 
277 void Manager::destroyObjects(const std::vector<const char*>& paths)
278 {
279     std::string p;
280 
281     for (const auto& path : paths)
282     {
283         p.assign(_root);
284         p.append(path);
285         _bus.emit_object_removed(p.c_str());
286         _refs.erase(p);
287     }
288 }
289 
290 void Manager::createObjects(
291     const std::map<sdbusplus::message::object_path, Object>& objs)
292 {
293     updateObjects(objs);
294 }
295 
296 std::any& Manager::getInterfaceHolder(const char* path, const char* interface)
297 {
298     return const_cast<std::any&>(
299         const_cast<const Manager*>(this)->getInterfaceHolder(path, interface));
300 }
301 
302 const std::any& Manager::getInterfaceHolder(const char* path,
303                                             const char* interface) const
304 {
305     std::string p{path};
306     auto oit = _refs.find(_root + p);
307     if (oit == _refs.end())
308         throw std::runtime_error(_root + p + " was not found");
309 
310     auto& obj = oit->second;
311     auto iit = obj.find(interface);
312     if (iit == obj.end())
313         throw std::runtime_error("interface was not found");
314 
315     return iit->second;
316 }
317 
318 void Manager::restore()
319 {
320     namespace fs = std::filesystem;
321 
322     if (!fs::exists(fs::path(PIM_PERSIST_PATH)))
323     {
324         return;
325     }
326 
327     static const std::string remove =
328         std::string(PIM_PERSIST_PATH) + INVENTORY_ROOT;
329 
330     std::map<sdbusplus::message::object_path, Object> objects;
331     for (const auto& dirent :
332          fs::recursive_directory_iterator(PIM_PERSIST_PATH))
333     {
334         const auto& path = dirent.path();
335         if (fs::is_regular_file(path))
336         {
337             auto ifaceName = path.filename().string();
338             auto objPath = path.parent_path().string();
339             objPath.erase(0, remove.length());
340             auto objit = objects.find(objPath);
341             Interface propertyMap{};
342             if (objects.end() != objit)
343             {
344                 auto& object = objit->second;
345                 object.emplace(std::move(ifaceName), std::move(propertyMap));
346             }
347             else
348             {
349                 Object object;
350                 object.emplace(std::move(ifaceName), std::move(propertyMap));
351                 objects.emplace(std::move(objPath), std::move(object));
352             }
353         }
354     }
355     if (!objects.empty())
356     {
357         auto restoreFromCache = true;
358         updateObjects(objects, restoreFromCache);
359     }
360 }
361 
362 } // namespace manager
363 } // namespace inventory
364 } // namespace phosphor
365 
366 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
367