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 <iostream>
17 #include <exception>
18 #include "manager.hpp"
19 
20 namespace phosphor
21 {
22 namespace inventory
23 {
24 namespace manager
25 {
26 namespace details
27 {
28 
29 /** @brief Fowrarding signal callback.
30  *
31  *  Extracts per-signal specific context and forwards the call to the manager
32  *  instance.
33  */
34 auto _signal(sd_bus_message *m, void *data, sd_bus_error *e) noexcept
35 {
36     try {
37         auto msg = sdbusplus::message::message(m);
38         auto &args = *static_cast<Manager::SigArg*>(data);
39         sd_bus_message_ref(m);
40         auto &mgr = *std::get<0>(args);
41         mgr.signal(
42                 msg,
43                 static_cast<const details::DbusSignal &>(
44                     *std::get<1>(args)),
45                 *std::get<2>(args));
46     }
47     catch (const std::exception &e) {
48         std::cerr << e.what() << std::endl;
49     }
50 
51     return 0;
52 }
53 
54 } // namespace details
55 
56 Manager::Manager(
57         sdbusplus::bus::bus &&bus,
58         const char *busname,
59         const char *root,
60         const char *iface) :
61     details::ServerObject<details::ManagerIface>(bus, root),
62     _shutdown(false),
63     _root(root),
64     _bus(std::move(bus)),
65     _manager(sdbusplus::server::manager::manager(_bus, root))
66 {
67     for (auto &group: _events)
68     {
69         for (auto pEvent: std::get<0>(group))
70         {
71             if (pEvent->type !=
72                     details::Event::Type::DBUS_SIGNAL)
73                 continue;
74 
75             // Create a callback context for this event group.
76             auto dbusEvent = static_cast<details::DbusSignal *>(
77                     pEvent.get());
78 
79             // Go ahead and store an iterator pointing at
80             // the event data to avoid lookups later since
81             // additional signal callbacks aren't added
82             // after the manager is constructed.
83             _sigargs.emplace_back(
84                     std::make_unique<SigArg>(
85                             this,
86                             dbusEvent,
87                             &group));
88 
89             // Register our callback and the context for
90             // each signal event.
91             _matches.emplace_back(
92                         _bus,
93                         std::get<0>(*dbusEvent),
94                         details::_signal,
95                         _sigargs.back().get());
96         }
97     }
98 
99     _bus.request_name(busname);
100 }
101 
102 void Manager::shutdown() noexcept
103 {
104     _shutdown = true;
105 }
106 
107 void Manager::run() noexcept
108 {
109     while(!_shutdown) {
110         try {
111             _bus.process_discard();
112             _bus.wait(5000000);
113         }
114         catch (const std::exception &e) {
115             std::cerr << e.what() << std::endl;
116         }
117     }
118 }
119 
120 void Manager::notify(std::string path, Object object)
121 {
122     try {
123         if(object.cbegin() == object.cend())
124             throw std::runtime_error(
125                     "No interfaces in " + path);
126 
127         path.insert(0, _root);
128 
129         auto obj = _refs.find(path);
130         if(obj != _refs.end())
131             throw std::runtime_error(
132                     obj->first + " already exists");
133 
134         // Create an interface holder for each interface
135         // provided by the client and group them into
136         // a container.
137         InterfaceComposite ref;
138 
139         for (auto &x: object) {
140             auto maker = _makers.find(x.first.c_str());
141 
142             if(maker == _makers.end())
143                 throw std::runtime_error(
144                         "Unimplemented interface: " + x.first);
145 
146             ref.emplace(x.first,
147                     (maker->second)(_bus, path.c_str()));
148         }
149 
150         // Hang on to a reference to the object (interfaces)
151         // so it stays on the bus, and so we can make calls
152         // to it if needed.
153         _refs.emplace(
154                 path, std::move(ref));
155     }
156     catch (const std::exception &e) {
157         std::cerr << e.what() << std::endl;
158     }
159 }
160 
161 void Manager::signal(
162         sdbusplus::message::message &msg,
163         const details::DbusSignal &event,
164         const EventInfo &info)
165 {
166     auto &filter = *std::get<1>(event);
167     auto &actions = std::get<1>(info);
168 
169     if(filter(msg, *this)) {
170         for (auto &action: actions)
171             (*action)(*this);
172     }
173 }
174 
175 void Manager::destroyObject(const char *path)
176 {
177     std::string p{path};
178     _refs.erase(_root + p);
179 }
180 
181 details::holder::Base& Manager::getInterfaceHolder(
182         const char *path, const char *interface)
183 {
184     return const_cast<const Manager *>(
185             this)->getInterfaceHolder(path, interface);
186 }
187 
188 details::holder::Base& Manager::getInterfaceHolder(
189         const char *path, const char *interface) const
190 {
191     std::string p{path};
192     auto oit = _refs.find(_root + p);
193     if(oit == _refs.end())
194         throw std::runtime_error(
195                 _root + p + " was not found");
196 
197     auto &obj = oit->second;
198     auto iit = obj.find(interface);
199     if(iit == obj.end())
200         throw std::runtime_error(
201                 "interface was not found");
202 
203     return *iit->second;
204 }
205 
206 } // namespace manager
207 } // namespace inventory
208 } // namespace phosphor
209 
210 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
211