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 std::any& Manager::getInterfaceHolder(const char* path, const char* interface)
284 {
285     return const_cast<std::any&>(
286         const_cast<const Manager*>(this)->getInterfaceHolder(path, interface));
287 }
288 
289 const std::any& Manager::getInterfaceHolder(const char* path,
290                                             const char* interface) const
291 {
292     std::string p{path};
293     auto oit = _refs.find(_root + p);
294     if (oit == _refs.end())
295         throw std::runtime_error(_root + p + " was not found");
296 
297     auto& obj = oit->second;
298     auto iit = obj.find(interface);
299     if (iit == obj.end())
300         throw std::runtime_error("interface was not found");
301 
302     return iit->second;
303 }
304 
305 void Manager::restore()
306 {
307     namespace fs = std::experimental::filesystem;
308 
309     if (!fs::exists(fs::path(PIM_PERSIST_PATH)))
310     {
311         return;
312     }
313 
314     static const std::string remove =
315         std::string(PIM_PERSIST_PATH) + INVENTORY_ROOT;
316 
317     std::map<sdbusplus::message::object_path, Object> objects;
318     for (const auto& dirent :
319          fs::recursive_directory_iterator(PIM_PERSIST_PATH))
320     {
321         const auto& path = dirent.path();
322         if (fs::is_regular_file(path))
323         {
324             auto ifaceName = path.filename().string();
325             auto objPath = path.parent_path().string();
326             objPath.erase(0, remove.length());
327             auto objit = objects.find(objPath);
328             Interface propertyMap{};
329             if (objects.end() != objit)
330             {
331                 auto& object = objit->second;
332                 object.emplace(std::move(ifaceName), std::move(propertyMap));
333             }
334             else
335             {
336                 Object object;
337                 object.emplace(std::move(ifaceName), std::move(propertyMap));
338                 objects.emplace(std::move(objPath), std::move(object));
339             }
340         }
341     }
342     if (!objects.empty())
343     {
344         auto restoreFromCache = true;
345         updateObjects(objects, restoreFromCache);
346     }
347 }
348 
349 } // namespace manager
350 } // namespace inventory
351 } // namespace phosphor
352 
353 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
354