1 #pragma once
2 
3 #include <sdbusplus/bus.hpp>
4 #include <sdbusplus/sdbus.hpp>
5 
6 #include <type_traits>
7 
8 namespace sdbusplus
9 {
10 
11 namespace server
12 {
13 
14 namespace object
15 {
16 
17 namespace details
18 {
19 
20 // Forward declaration.
21 template <class... Args>
22 struct compose;
23 
24 } // namespace details
25 
26 /** Class to compose multiple dbus interfaces and object signals.
27  *
28  *  Any number of classes representing a dbus interface may be composed into
29  *  a single dbus object.  The interfaces will be created first and hooked
30  *  into the object space via the 'add_object_vtable' calls.  Afterwards,
31  *  a signal will be emitted for the whole object to indicate all new
32  *  interfaces via 'sd_bus_emit_object_added'.
33  *
34  *  Similar, on destruction, the interfaces are removed (unref'd) and the
35  *  'sd_bus_emit_object_removed' signals are emitted.
36  *
37  */
38 template <class... Args>
39 struct object :
40     details::compose<Args...>,
41     private sdbusplus::bus::details::bus_friend
42 {
43     /* Define all of the basic class operations:
44      *     Not allowed:
45      *         - Default constructor to avoid nullptrs.
46      *         - Copy operations due to internal unique_ptr.
47      *         - Move operations.
48      *     Allowed:
49      *         - Destructor.
50      */
51     object() = delete;
52     object(const object&) = delete;
53     object& operator=(const object&) = delete;
54     object(object&&) = delete;
55     object& operator=(object&&) = delete;
56 
57     enum class action
58     {
59         /** sd_bus_emit_object_{added, removed} */
60         emit_object_added,
61         /** sd_bus_emit_interfaces_{added, removed} */
62         emit_interface_added,
63         /** no automatic added signal, but sd_bus_emit_object_removed on
64          *  destruct */
65         defer_emit,
66         /** no interface signals */
67         emit_no_signals,
68     };
69 
70     /** Construct an 'object' on a bus with a path.
71      *
72      *  @param[in] bus - The bus to place the object on.
73      *  @param[in] path - The path the object resides at.
74      *  @param[in] act - Set to the desired InterfacesAdded signal behavior.
75      */
objectsdbusplus::server::object::object76     object(bus_t& bus, const char* path,
77            action act = action::emit_object_added) :
78         details::compose<Args...>(bus, path),
79         __sdbusplus_server_object_bus(get_busp(bus), bus.getInterface()),
80         __sdbusplus_server_object_path(path),
81         __sdbusplus_server_object_intf(bus.getInterface())
82     {
83         // Default ctor
84         check_action(act);
85     }
86 
~objectsdbusplus::server::object::object87     ~object() override
88     {
89         if (__sdbusplus_server_object_signalstate != action::emit_no_signals)
90         {
91             __sdbusplus_server_object_intf->sd_bus_emit_object_removed(
92                 get_busp(__sdbusplus_server_object_bus),
93                 __sdbusplus_server_object_path.c_str());
94         }
95     }
96 
97     /** Emit the 'object-added' signal, if not already sent. */
emit_object_addedsdbusplus::server::object::object98     void emit_object_added()
99     {
100         if (__sdbusplus_server_object_signalstate == action::defer_emit)
101         {
102             __sdbusplus_server_object_intf->sd_bus_emit_object_added(
103                 get_busp(__sdbusplus_server_object_bus),
104                 __sdbusplus_server_object_path.c_str());
105             __sdbusplus_server_object_signalstate = action::emit_object_added;
106         }
107     }
108 
109   private:
110     // These member names are purposefully chosen as long and, hopefully,
111     // unique.  Since an object is 'composed' via multiple-inheritance,
112     // all members need to have unique names to ensure there is no
113     // ambiguity.
114     bus_t __sdbusplus_server_object_bus;
115     std::string __sdbusplus_server_object_path;
116     action __sdbusplus_server_object_signalstate = action::defer_emit;
117     SdBusInterface* __sdbusplus_server_object_intf;
118 
119     /** Check and run the action */
check_actionsdbusplus::server::object::object120     void check_action(action act)
121     {
122         switch (act)
123         {
124             case action::emit_object_added:
125                 // We are wanting to call emit_object_added to set up in
126                 // deferred state temporarily and then emit the signal.
127                 __sdbusplus_server_object_signalstate = action::defer_emit;
128                 emit_object_added();
129                 break;
130 
131             case action::emit_interface_added:
132                 details::compose<Args...>::maybe_emit_iface_added();
133                 // If we are emitting at an interface level, we should never
134                 // also emit at the object level.
135                 __sdbusplus_server_object_signalstate = action::emit_no_signals;
136                 break;
137 
138             case action::defer_emit:
139                 __sdbusplus_server_object_signalstate = action::defer_emit;
140                 break;
141 
142             case action::emit_no_signals:
143                 __sdbusplus_server_object_signalstate = action::emit_no_signals;
144                 break;
145         }
146     }
147 };
148 
149 } // namespace object
150 
151 // Type alias for object_t.
152 template <class... Args>
153 using object_t = object::object<Args...>;
154 
155 namespace object::details
156 {
157 
158 /** Template to identify if an inheritance is a "normal" type or a nested
159  *  sdbusplus::object.
160  */
161 template <class T>
162 struct compose_inherit
163 {
164     // This is a normal type.
165     using type = T;
166 };
167 
168 /** Template specialization for the sdbusplus::object nesting. */
169 template <class... Args>
170 struct compose_inherit<object<Args...>>
171 {
172     // Unravel the inheritance with a recursive compose.
173     using type = compose<Args...>;
174 };
175 
176 /** std-style _t alias for compose_inherit. */
177 template <class... Args>
178 using compose_inherit_t = typename compose_inherit<Args...>::type;
179 
180 template <class... Args>
181 struct compose : compose_inherit_t<Args>...
182 {
composesdbusplus::server::object::details::compose183     compose(bus_t& bus, const char* path) :
184         compose_inherit<Args>::type(bus, path)... {};
185 
186     /** Call emit_added on all the composed types. */
maybe_emit_iface_addedsdbusplus::server::object::details::compose187     void maybe_emit_iface_added()
188     {
189         (try_emit<compose_inherit_t<Args>>(), ...);
190     }
191 
192   protected:
193     /** Call emit_added for an individual composed type. */
194     template <class T>
try_emitsdbusplus::server::object::details::compose195     void try_emit()
196     {
197         // If T is a normal class with emit_added, call it.
198         if constexpr (requires(T& t) { t.emit_added(); })
199         {
200             this->T::emit_added();
201         }
202         // If T is a recursive object_t, call its maybe_emit_iface_added.
203         if constexpr (requires(T& t) { t.maybe_emit_iface_added(); })
204         {
205             this->T::maybe_emit_iface_added();
206         }
207     }
208 };
209 
210 } // namespace object::details
211 
212 } // namespace server
213 } // namespace sdbusplus
214