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 <experimental/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 {
64     for (auto& group : _events)
65     {
66         for (auto pEvent : std::get<std::vector<EventBasePtr>>(group))
67         {
68             if (pEvent->type != Event::Type::DBUS_SIGNAL)
69             {
70                 continue;
71             }
72 
73             // Create a callback context for this event group.
74             auto dbusEvent = static_cast<DbusSignal*>(pEvent.get());
75 
76             // Go ahead and store an iterator pointing at
77             // the event data to avoid lookups later since
78             // additional signal callbacks aren't added
79             // after the manager is constructed.
80             _sigargs.emplace_back(
81                 std::make_unique<SigArg>(this, dbusEvent, &group));
82 
83             // Register our callback and the context for
84             // each signal event.
85             _matches.emplace_back(_bus, dbusEvent->signature, _signal,
86                                   _sigargs.back().get());
87         }
88     }
89 
90     _bus.request_name(busname);
91 
92     // Restore any persistent inventory
93     restore();
94 }
95 
96 void Manager::shutdown() noexcept
97 {
98     _shutdown = true;
99 }
100 
101 void Manager::run() noexcept
102 {
103     sdbusplus::message::message 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     while (!_shutdown)
118     {
119         try
120         {
121             _bus.process_discard();
122             _bus.wait((5000000us).count());
123         }
124         catch (const std::exception& e)
125         {
126             std::cerr << e.what() << std::endl;
127         }
128     }
129 }
130 
131 void Manager::updateInterfaces(const sdbusplus::message::object_path& path,
132                                const Object& interfaces,
133                                ObjectReferences::iterator pos, bool newObject,
134                                bool restoreFromCache)
135 {
136     auto& refaces = pos->second;
137     auto ifaceit = interfaces.cbegin();
138     auto opsit = _makers.cbegin();
139     auto refaceit = refaces.begin();
140     std::vector<std::string> signals;
141 
142     while (ifaceit != interfaces.cend())
143     {
144         try
145         {
146             // Find the binding ops for this interface.
147             opsit = std::lower_bound(opsit, _makers.cend(), ifaceit->first,
148                                      compareFirst(_makers.key_comp()));
149 
150             if (opsit == _makers.cend() || opsit->first != ifaceit->first)
151             {
152                 // This interface is not supported.
153                 throw InterfaceError("Encountered unsupported interface.",
154                                      ifaceit->first);
155             }
156 
157             // Find the binding insertion point or the binding to update.
158             refaceit = std::lower_bound(refaceit, refaces.end(), ifaceit->first,
159                                         compareFirst(refaces.key_comp()));
160 
161             if (refaceit == refaces.end() || refaceit->first != ifaceit->first)
162             {
163                 // Add the new interface.
164                 auto& ctor = std::get<MakerType>(opsit->second);
165                 refaceit = refaces.insert(
166                     refaceit,
167                     std::make_pair(ifaceit->first, ctor(_bus, path.str.c_str(),
168                                                         ifaceit->second)));
169                 signals.push_back(ifaceit->first);
170             }
171             else
172             {
173                 // Set the new property values.
174                 auto& assign = std::get<AssignerType>(opsit->second);
175                 assign(ifaceit->second, refaceit->second);
176             }
177             if (!restoreFromCache)
178             {
179                 auto& serialize = std::get<SerializerType>(opsit->second);
180                 serialize(path, ifaceit->first, refaceit->second);
181             }
182             else
183             {
184                 auto& deserialize = std::get<DeserializerType>(opsit->second);
185                 deserialize(path, ifaceit->first, refaceit->second);
186             }
187         }
188         catch (const InterfaceError& e)
189         {
190             // Reset the binding ops iterator since we are
191             // at the end.
192             opsit = _makers.cbegin();
193             e.log();
194         }
195 
196         ++ifaceit;
197     }
198 
199     if (newObject)
200     {
201         _bus.emit_object_added(path.str.c_str());
202     }
203     else if (!signals.empty())
204     {
205         _bus.emit_interfaces_added(path.str.c_str(), signals);
206     }
207 }
208 
209 void Manager::updateObjects(
210     const std::map<sdbusplus::message::object_path, Object>& objs,
211     bool restoreFromCache)
212 {
213     auto objit = objs.cbegin();
214     auto refit = _refs.begin();
215     std::string absPath;
216     bool newObj;
217 
218     while (objit != objs.cend())
219     {
220         // Find the insertion point or the object to update.
221         refit = std::lower_bound(refit, _refs.end(), objit->first,
222                                  compareFirst(RelPathCompare(_root)));
223 
224         absPath.assign(_root);
225         absPath.append(objit->first);
226 
227         newObj = false;
228         if (refit == _refs.end() || refit->first != absPath)
229         {
230             refit = _refs.insert(
231                 refit, std::make_pair(absPath, decltype(_refs)::mapped_type()));
232             newObj = true;
233         }
234 
235         updateInterfaces(absPath, objit->second, refit, newObj,
236                          restoreFromCache);
237         ++objit;
238     }
239 }
240 
241 void Manager::notify(std::map<sdbusplus::message::object_path, Object> objs)
242 {
243     updateObjects(objs);
244 }
245 
246 void Manager::handleEvent(sdbusplus::message::message& msg, const Event& event,
247                           const EventInfo& info)
248 {
249     auto& actions = std::get<1>(info);
250 
251     for (auto& f : event)
252     {
253         if (!f(_bus, msg, *this))
254         {
255             return;
256         }
257     }
258     for (auto& action : actions)
259     {
260         action(_bus, *this);
261     }
262 }
263 
264 void Manager::destroyObjects(const std::vector<const char*>& paths)
265 {
266     std::string p;
267 
268     for (const auto& path : paths)
269     {
270         p.assign(_root);
271         p.append(path);
272         _bus.emit_object_removed(p.c_str());
273         _refs.erase(p);
274     }
275 }
276 
277 void Manager::createObjects(
278     const std::map<sdbusplus::message::object_path, Object>& objs)
279 {
280     updateObjects(objs);
281 }
282 
283 any_ns::any& Manager::getInterfaceHolder(const char* path,
284                                          const char* interface)
285 {
286     return const_cast<any_ns::any&>(
287         const_cast<const Manager*>(this)->getInterfaceHolder(path, interface));
288 }
289 
290 const any_ns::any& Manager::getInterfaceHolder(const char* path,
291                                                const char* interface) const
292 {
293     std::string p{path};
294     auto oit = _refs.find(_root + p);
295     if (oit == _refs.end())
296         throw std::runtime_error(_root + p + " was not found");
297 
298     auto& obj = oit->second;
299     auto iit = obj.find(interface);
300     if (iit == obj.end())
301         throw std::runtime_error("interface was not found");
302 
303     return iit->second;
304 }
305 
306 void Manager::restore()
307 {
308     namespace fs = std::experimental::filesystem;
309 
310     if (!fs::exists(fs::path(PIM_PERSIST_PATH)))
311     {
312         return;
313     }
314 
315     static const std::string remove =
316         std::string(PIM_PERSIST_PATH) + INVENTORY_ROOT;
317 
318     std::map<sdbusplus::message::object_path, Object> objects;
319     for (const auto& dirent :
320          fs::recursive_directory_iterator(PIM_PERSIST_PATH))
321     {
322         const auto& path = dirent.path();
323         if (fs::is_regular_file(path))
324         {
325             auto ifaceName = path.filename().string();
326             auto objPath = path.parent_path().string();
327             objPath.erase(0, remove.length());
328             auto objit = objects.find(objPath);
329             Interface propertyMap{};
330             if (objects.end() != objit)
331             {
332                 auto& object = objit->second;
333                 object.emplace(std::move(ifaceName), std::move(propertyMap));
334             }
335             else
336             {
337                 Object object;
338                 object.emplace(std::move(ifaceName), std::move(propertyMap));
339                 objects.emplace(std::move(objPath), std::move(object));
340             }
341         }
342     }
343     if (!objects.empty())
344     {
345         auto restoreFromCache = true;
346         updateObjects(objects, restoreFromCache);
347     }
348 }
349 
350 } // namespace manager
351 } // namespace inventory
352 } // namespace phosphor
353 
354 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
355