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