149aefb31SBrad Bishop /**
249aefb31SBrad Bishop  * Copyright © 2016 IBM Corporation
349aefb31SBrad Bishop  *
449aefb31SBrad Bishop  * Licensed under the Apache License, Version 2.0 (the "License");
549aefb31SBrad Bishop  * you may not use this file except in compliance with the License.
649aefb31SBrad Bishop  * You may obtain a copy of the License at
749aefb31SBrad Bishop  *
849aefb31SBrad Bishop  *     http://www.apache.org/licenses/LICENSE-2.0
949aefb31SBrad Bishop  *
1049aefb31SBrad Bishop  * Unless required by applicable law or agreed to in writing, software
1149aefb31SBrad Bishop  * distributed under the License is distributed on an "AS IS" BASIS,
1249aefb31SBrad Bishop  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1349aefb31SBrad Bishop  * See the License for the specific language governing permissions and
1449aefb31SBrad Bishop  * limitations under the License.
1549aefb31SBrad Bishop  */
1649aefb31SBrad Bishop #include "manager.hpp"
17*a680d1efSPatrick Venture 
1879ccaf77SBrad Bishop #include "errors.hpp"
1949aefb31SBrad Bishop 
20*a680d1efSPatrick Venture #include <algorithm>
21*a680d1efSPatrick Venture #include <chrono>
22*a680d1efSPatrick Venture #include <exception>
23*a680d1efSPatrick Venture #include <experimental/filesystem>
24*a680d1efSPatrick Venture #include <iostream>
25*a680d1efSPatrick Venture #include <phosphor-logging/log.hpp>
26*a680d1efSPatrick Venture 
2724424983SBrad Bishop using namespace std::literals::chrono_literals;
2824424983SBrad Bishop 
2949aefb31SBrad Bishop namespace phosphor
3049aefb31SBrad Bishop {
3149aefb31SBrad Bishop namespace inventory
3249aefb31SBrad Bishop {
3349aefb31SBrad Bishop namespace manager
3449aefb31SBrad Bishop {
3549aefb31SBrad Bishop /** @brief Fowrarding signal callback.
3649aefb31SBrad Bishop  *
3749aefb31SBrad Bishop  *  Extracts per-signal specific context and forwards the call to the manager
3849aefb31SBrad Bishop  *  instance.
3949aefb31SBrad Bishop  */
4049aefb31SBrad Bishop auto _signal(sd_bus_message* m, void* data, sd_bus_error* e) noexcept
4149aefb31SBrad Bishop {
427b33777bSBrad Bishop     try
437b33777bSBrad Bishop     {
4449aefb31SBrad Bishop         auto msg = sdbusplus::message::message(m);
4549aefb31SBrad Bishop         auto& args = *static_cast<Manager::SigArg*>(data);
4649aefb31SBrad Bishop         sd_bus_message_ref(m);
4749aefb31SBrad Bishop         auto& mgr = *std::get<0>(args);
48615b2a8fSBrad Bishop         mgr.handleEvent(msg, static_cast<const DbusSignal&>(*std::get<1>(args)),
4968c80839SBrad Bishop                         *std::get<2>(args));
5049aefb31SBrad Bishop     }
517b33777bSBrad Bishop     catch (const std::exception& e)
527b33777bSBrad Bishop     {
5349aefb31SBrad Bishop         std::cerr << e.what() << std::endl;
5449aefb31SBrad Bishop     }
5549aefb31SBrad Bishop 
5649aefb31SBrad Bishop     return 0;
5749aefb31SBrad Bishop }
5849aefb31SBrad Bishop 
59615b2a8fSBrad Bishop Manager::Manager(sdbusplus::bus::bus&& bus, const char* busname,
60615b2a8fSBrad Bishop                  const char* root, const char* iface) :
6112f8a3c8SBrad Bishop     ServerObject<ManagerIface>(bus, root),
62615b2a8fSBrad Bishop     _shutdown(false), _root(root), _bus(std::move(bus)), _manager(_bus, root)
6349aefb31SBrad Bishop {
6468c80839SBrad Bishop     for (auto& group : _events)
6568c80839SBrad Bishop     {
66615b2a8fSBrad Bishop         for (auto pEvent : std::get<std::vector<EventBasePtr>>(group))
6768c80839SBrad Bishop         {
68615b2a8fSBrad Bishop             if (pEvent->type != Event::Type::DBUS_SIGNAL)
697b33777bSBrad Bishop             {
704f20a3e3SBrad Bishop                 continue;
717b33777bSBrad Bishop             }
724f20a3e3SBrad Bishop 
7368c80839SBrad Bishop             // Create a callback context for this event group.
74615b2a8fSBrad Bishop             auto dbusEvent = static_cast<DbusSignal*>(pEvent.get());
7568c80839SBrad Bishop 
7668c80839SBrad Bishop             // Go ahead and store an iterator pointing at
7768c80839SBrad Bishop             // the event data to avoid lookups later since
7868c80839SBrad Bishop             // additional signal callbacks aren't added
7968c80839SBrad Bishop             // after the manager is constructed.
8049aefb31SBrad Bishop             _sigargs.emplace_back(
81615b2a8fSBrad Bishop                 std::make_unique<SigArg>(this, dbusEvent, &group));
8268c80839SBrad Bishop 
8349aefb31SBrad Bishop             // Register our callback and the context for
8468c80839SBrad Bishop             // each signal event.
85615b2a8fSBrad Bishop             _matches.emplace_back(_bus, dbusEvent->signature, _signal,
861ab880a1SBrad Bishop                                   _sigargs.back().get());
8749aefb31SBrad Bishop         }
8868c80839SBrad Bishop     }
8949aefb31SBrad Bishop 
9049aefb31SBrad Bishop     _bus.request_name(busname);
91b28990f3SDeepak Kodihalli 
92b28990f3SDeepak Kodihalli     // Restore any persistent inventory
93b28990f3SDeepak Kodihalli     restore();
9449aefb31SBrad Bishop }
9549aefb31SBrad Bishop 
9649aefb31SBrad Bishop void Manager::shutdown() noexcept
9749aefb31SBrad Bishop {
9849aefb31SBrad Bishop     _shutdown = true;
9949aefb31SBrad Bishop }
10049aefb31SBrad Bishop 
10149aefb31SBrad Bishop void Manager::run() noexcept
10249aefb31SBrad Bishop {
1033e4a19a3SBrad Bishop     sdbusplus::message::message unusedMsg{nullptr};
1043e4a19a3SBrad Bishop 
1053e4a19a3SBrad Bishop     // Run startup events.
1063e4a19a3SBrad Bishop     for (auto& group : _events)
1073e4a19a3SBrad Bishop     {
108615b2a8fSBrad Bishop         for (auto pEvent : std::get<std::vector<EventBasePtr>>(group))
1093e4a19a3SBrad Bishop         {
110615b2a8fSBrad Bishop             if (pEvent->type == Event::Type::STARTUP)
1113e4a19a3SBrad Bishop             {
1123e4a19a3SBrad Bishop                 handleEvent(unusedMsg, *pEvent, group);
1133e4a19a3SBrad Bishop             }
1143e4a19a3SBrad Bishop         }
1153e4a19a3SBrad Bishop     }
1163e4a19a3SBrad Bishop 
1177b33777bSBrad Bishop     while (!_shutdown)
1187b33777bSBrad Bishop     {
1197b33777bSBrad Bishop         try
1207b33777bSBrad Bishop         {
12149aefb31SBrad Bishop             _bus.process_discard();
12224424983SBrad Bishop             _bus.wait((5000000us).count());
12349aefb31SBrad Bishop         }
1247b33777bSBrad Bishop         catch (const std::exception& e)
1257b33777bSBrad Bishop         {
12649aefb31SBrad Bishop             std::cerr << e.what() << std::endl;
12749aefb31SBrad Bishop         }
12849aefb31SBrad Bishop     }
12949aefb31SBrad Bishop }
13049aefb31SBrad Bishop 
131615b2a8fSBrad Bishop void Manager::updateInterfaces(const sdbusplus::message::object_path& path,
13279ccaf77SBrad Bishop                                const Object& interfaces,
133615b2a8fSBrad Bishop                                ObjectReferences::iterator pos, bool newObject,
134b28990f3SDeepak Kodihalli                                bool restoreFromCache)
13579ccaf77SBrad Bishop {
13679ccaf77SBrad Bishop     auto& refaces = pos->second;
13779ccaf77SBrad Bishop     auto ifaceit = interfaces.cbegin();
13879ccaf77SBrad Bishop     auto opsit = _makers.cbegin();
13979ccaf77SBrad Bishop     auto refaceit = refaces.begin();
14079ccaf77SBrad Bishop     std::vector<std::string> signals;
14179ccaf77SBrad Bishop 
14279ccaf77SBrad Bishop     while (ifaceit != interfaces.cend())
14379ccaf77SBrad Bishop     {
14479ccaf77SBrad Bishop         try
14579ccaf77SBrad Bishop         {
14679ccaf77SBrad Bishop             // Find the binding ops for this interface.
147615b2a8fSBrad Bishop             opsit = std::lower_bound(opsit, _makers.cend(), ifaceit->first,
14879ccaf77SBrad Bishop                                      compareFirst(_makers.key_comp()));
14979ccaf77SBrad Bishop 
15079ccaf77SBrad Bishop             if (opsit == _makers.cend() || opsit->first != ifaceit->first)
15179ccaf77SBrad Bishop             {
15279ccaf77SBrad Bishop                 // This interface is not supported.
153615b2a8fSBrad Bishop                 throw InterfaceError("Encountered unsupported interface.",
15479ccaf77SBrad Bishop                                      ifaceit->first);
15579ccaf77SBrad Bishop             }
15679ccaf77SBrad Bishop 
15779ccaf77SBrad Bishop             // Find the binding insertion point or the binding to update.
158615b2a8fSBrad Bishop             refaceit = std::lower_bound(refaceit, refaces.end(), ifaceit->first,
15979ccaf77SBrad Bishop                                         compareFirst(refaces.key_comp()));
16079ccaf77SBrad Bishop 
16179ccaf77SBrad Bishop             if (refaceit == refaces.end() || refaceit->first != ifaceit->first)
16279ccaf77SBrad Bishop             {
16379ccaf77SBrad Bishop                 // Add the new interface.
16479ccaf77SBrad Bishop                 auto& ctor = std::get<MakerType>(opsit->second);
16579ccaf77SBrad Bishop                 refaceit = refaces.insert(
16679ccaf77SBrad Bishop                     refaceit,
167615b2a8fSBrad Bishop                     std::make_pair(ifaceit->first, ctor(_bus, path.str.c_str(),
16879ccaf77SBrad Bishop                                                         ifaceit->second)));
16979ccaf77SBrad Bishop                 signals.push_back(ifaceit->first);
17079ccaf77SBrad Bishop             }
17179ccaf77SBrad Bishop             else
17279ccaf77SBrad Bishop             {
17379ccaf77SBrad Bishop                 // Set the new property values.
17479ccaf77SBrad Bishop                 auto& assign = std::get<AssignerType>(opsit->second);
17579ccaf77SBrad Bishop                 assign(ifaceit->second, refaceit->second);
17679ccaf77SBrad Bishop             }
177b28990f3SDeepak Kodihalli             if (!restoreFromCache)
178b28990f3SDeepak Kodihalli             {
1796620e98dSDeepak Kodihalli                 auto& serialize = std::get<SerializerType>(opsit->second);
1806620e98dSDeepak Kodihalli                 serialize(path, ifaceit->first, refaceit->second);
18179ccaf77SBrad Bishop             }
182b28990f3SDeepak Kodihalli             else
183b28990f3SDeepak Kodihalli             {
184b28990f3SDeepak Kodihalli                 auto& deserialize = std::get<DeserializerType>(opsit->second);
185b28990f3SDeepak Kodihalli                 deserialize(path, ifaceit->first, refaceit->second);
186b28990f3SDeepak Kodihalli             }
187b28990f3SDeepak Kodihalli         }
18879ccaf77SBrad Bishop         catch (const InterfaceError& e)
18979ccaf77SBrad Bishop         {
19079ccaf77SBrad Bishop             // Reset the binding ops iterator since we are
19179ccaf77SBrad Bishop             // at the end.
19279ccaf77SBrad Bishop             opsit = _makers.cbegin();
19379ccaf77SBrad Bishop             e.log();
19479ccaf77SBrad Bishop         }
19579ccaf77SBrad Bishop 
19679ccaf77SBrad Bishop         ++ifaceit;
19779ccaf77SBrad Bishop     }
19879ccaf77SBrad Bishop 
19979ccaf77SBrad Bishop     if (newObject)
20079ccaf77SBrad Bishop     {
20179ccaf77SBrad Bishop         _bus.emit_object_added(path.str.c_str());
20279ccaf77SBrad Bishop     }
20379ccaf77SBrad Bishop     else if (!signals.empty())
20479ccaf77SBrad Bishop     {
205a8ff8154SGunnar Mills         _bus.emit_interfaces_added(path.str.c_str(), signals);
20679ccaf77SBrad Bishop     }
20779ccaf77SBrad Bishop }
20879ccaf77SBrad Bishop 
20979ccaf77SBrad Bishop void Manager::updateObjects(
210b28990f3SDeepak Kodihalli     const std::map<sdbusplus::message::object_path, Object>& objs,
211b28990f3SDeepak Kodihalli     bool restoreFromCache)
21279ccaf77SBrad Bishop {
21379ccaf77SBrad Bishop     auto objit = objs.cbegin();
21479ccaf77SBrad Bishop     auto refit = _refs.begin();
21579ccaf77SBrad Bishop     std::string absPath;
21679ccaf77SBrad Bishop     bool newObj;
21779ccaf77SBrad Bishop 
21879ccaf77SBrad Bishop     while (objit != objs.cend())
21979ccaf77SBrad Bishop     {
22079ccaf77SBrad Bishop         // Find the insertion point or the object to update.
221615b2a8fSBrad Bishop         refit = std::lower_bound(refit, _refs.end(), objit->first,
22279ccaf77SBrad Bishop                                  compareFirst(RelPathCompare(_root)));
22379ccaf77SBrad Bishop 
22479ccaf77SBrad Bishop         absPath.assign(_root);
22579ccaf77SBrad Bishop         absPath.append(objit->first);
22679ccaf77SBrad Bishop 
22779ccaf77SBrad Bishop         newObj = false;
22879ccaf77SBrad Bishop         if (refit == _refs.end() || refit->first != absPath)
22979ccaf77SBrad Bishop         {
23079ccaf77SBrad Bishop             refit = _refs.insert(
231615b2a8fSBrad Bishop                 refit, std::make_pair(absPath, decltype(_refs)::mapped_type()));
23279ccaf77SBrad Bishop             newObj = true;
23379ccaf77SBrad Bishop         }
23479ccaf77SBrad Bishop 
235b28990f3SDeepak Kodihalli         updateInterfaces(absPath, objit->second, refit, newObj,
236b28990f3SDeepak Kodihalli                          restoreFromCache);
23779ccaf77SBrad Bishop         ++objit;
23879ccaf77SBrad Bishop     }
23979ccaf77SBrad Bishop }
24079ccaf77SBrad Bishop 
24103f4cd95SBrad Bishop void Manager::notify(std::map<sdbusplus::message::object_path, Object> objs)
24249aefb31SBrad Bishop {
243cda036f7SBrad Bishop     updateObjects(objs);
24449aefb31SBrad Bishop }
24549aefb31SBrad Bishop 
246615b2a8fSBrad Bishop void Manager::handleEvent(sdbusplus::message::message& msg, const Event& event,
24768c80839SBrad Bishop                           const EventInfo& info)
24849aefb31SBrad Bishop {
24968c80839SBrad Bishop     auto& actions = std::get<1>(info);
2503d57f507SBrad Bishop 
25148547a85SBrad Bishop     for (auto& f : event)
2527b33777bSBrad Bishop     {
25307934a64SBrad Bishop         if (!f(_bus, msg, *this))
254064c94a6SBrad Bishop         {
255064c94a6SBrad Bishop             return;
256064c94a6SBrad Bishop         }
257064c94a6SBrad Bishop     }
2589007432aSBrad Bishop     for (auto& action : actions)
2597b33777bSBrad Bishop     {
26007934a64SBrad Bishop         action(_bus, *this);
2613d57f507SBrad Bishop     }
26249aefb31SBrad Bishop }
26349aefb31SBrad Bishop 
264615b2a8fSBrad Bishop void Manager::destroyObjects(const std::vector<const char*>& paths)
2657b7e712cSBrad Bishop {
266a5cc34c2SBrad Bishop     std::string p;
267a5cc34c2SBrad Bishop 
2687b7e712cSBrad Bishop     for (const auto& path : paths)
269656a7d00SBrad Bishop     {
270a5cc34c2SBrad Bishop         p.assign(_root);
271a5cc34c2SBrad Bishop         p.append(path);
272a5cc34c2SBrad Bishop         _bus.emit_object_removed(p.c_str());
273a5cc34c2SBrad Bishop         _refs.erase(p);
274656a7d00SBrad Bishop     }
2757b7e712cSBrad Bishop }
276656a7d00SBrad Bishop 
277eb68a687SBrad Bishop void Manager::createObjects(
278eb68a687SBrad Bishop     const std::map<sdbusplus::message::object_path, Object>& objs)
279eb68a687SBrad Bishop {
280cda036f7SBrad Bishop     updateObjects(objs);
281eb68a687SBrad Bishop }
282eb68a687SBrad Bishop 
283615b2a8fSBrad Bishop any_ns::any& Manager::getInterfaceHolder(const char* path,
284615b2a8fSBrad Bishop                                          const char* interface)
285b83a21eaSBrad Bishop {
286150147aeSBrad Bishop     return const_cast<any_ns::any&>(
287615b2a8fSBrad Bishop         const_cast<const Manager*>(this)->getInterfaceHolder(path, interface));
288b83a21eaSBrad Bishop }
289b83a21eaSBrad Bishop 
290615b2a8fSBrad Bishop const any_ns::any& Manager::getInterfaceHolder(const char* path,
291615b2a8fSBrad Bishop                                                const char* interface) const
292b83a21eaSBrad Bishop {
293b83a21eaSBrad Bishop     std::string p{path};
294b83a21eaSBrad Bishop     auto oit = _refs.find(_root + p);
295b83a21eaSBrad Bishop     if (oit == _refs.end())
296615b2a8fSBrad Bishop         throw std::runtime_error(_root + p + " was not found");
297b83a21eaSBrad Bishop 
298b83a21eaSBrad Bishop     auto& obj = oit->second;
299b83a21eaSBrad Bishop     auto iit = obj.find(interface);
300b83a21eaSBrad Bishop     if (iit == obj.end())
301615b2a8fSBrad Bishop         throw std::runtime_error("interface was not found");
302b83a21eaSBrad Bishop 
303150147aeSBrad Bishop     return iit->second;
304b83a21eaSBrad Bishop }
305b83a21eaSBrad Bishop 
306b28990f3SDeepak Kodihalli void Manager::restore()
307b28990f3SDeepak Kodihalli {
308b28990f3SDeepak Kodihalli     namespace fs = std::experimental::filesystem;
309b28990f3SDeepak Kodihalli 
310b28990f3SDeepak Kodihalli     if (!fs::exists(fs::path(PIM_PERSIST_PATH)))
311b28990f3SDeepak Kodihalli     {
312b28990f3SDeepak Kodihalli         return;
313b28990f3SDeepak Kodihalli     }
314b28990f3SDeepak Kodihalli 
315b28990f3SDeepak Kodihalli     static const std::string remove =
316b28990f3SDeepak Kodihalli         std::string(PIM_PERSIST_PATH) + INVENTORY_ROOT;
317b28990f3SDeepak Kodihalli 
318b28990f3SDeepak Kodihalli     std::map<sdbusplus::message::object_path, Object> objects;
319b28990f3SDeepak Kodihalli     for (const auto& dirent :
320b28990f3SDeepak Kodihalli          fs::recursive_directory_iterator(PIM_PERSIST_PATH))
321b28990f3SDeepak Kodihalli     {
322b28990f3SDeepak Kodihalli         const auto& path = dirent.path();
323b28990f3SDeepak Kodihalli         if (fs::is_regular_file(path))
324b28990f3SDeepak Kodihalli         {
325b28990f3SDeepak Kodihalli             auto ifaceName = path.filename().string();
326b28990f3SDeepak Kodihalli             auto objPath = path.parent_path().string();
327b28990f3SDeepak Kodihalli             objPath.erase(0, remove.length());
328b28990f3SDeepak Kodihalli             auto objit = objects.find(objPath);
329b28990f3SDeepak Kodihalli             Interface propertyMap{};
330b28990f3SDeepak Kodihalli             if (objects.end() != objit)
331b28990f3SDeepak Kodihalli             {
332b28990f3SDeepak Kodihalli                 auto& object = objit->second;
333b28990f3SDeepak Kodihalli                 object.emplace(std::move(ifaceName), std::move(propertyMap));
334b28990f3SDeepak Kodihalli             }
335b28990f3SDeepak Kodihalli             else
336b28990f3SDeepak Kodihalli             {
337b28990f3SDeepak Kodihalli                 Object object;
338b28990f3SDeepak Kodihalli                 object.emplace(std::move(ifaceName), std::move(propertyMap));
339b28990f3SDeepak Kodihalli                 objects.emplace(std::move(objPath), std::move(object));
340b28990f3SDeepak Kodihalli             }
341b28990f3SDeepak Kodihalli         }
342b28990f3SDeepak Kodihalli     }
343b28990f3SDeepak Kodihalli     if (!objects.empty())
344b28990f3SDeepak Kodihalli     {
345b28990f3SDeepak Kodihalli         auto restoreFromCache = true;
346b28990f3SDeepak Kodihalli         updateObjects(objects, restoreFromCache);
347b28990f3SDeepak Kodihalli     }
348b28990f3SDeepak Kodihalli }
349b28990f3SDeepak Kodihalli 
35049aefb31SBrad Bishop } // namespace manager
35149aefb31SBrad Bishop } // namespace inventory
35249aefb31SBrad Bishop } // namespace phosphor
35349aefb31SBrad Bishop 
35449aefb31SBrad Bishop // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
355