1 #pragma once 2 3 #include <sdbusplus/async/callback.hpp> 4 #include <sdbusplus/async/context.hpp> 5 #include <sdbusplus/message.hpp> 6 7 #include <string> 8 #include <string_view> 9 #include <type_traits> 10 #include <unordered_map> 11 #include <variant> 12 13 namespace sdbusplus::async 14 { 15 namespace proxy_ns 16 { 17 /** A (client-side) proxy to a dbus object. 18 * 19 * A dbus object is referenced by 3 address pieces: 20 * - The service hosting the object. 21 * - The path the object resides at. 22 * - The interface the object implements. 23 * The proxy is a holder of these 3 addresses. 24 * 25 * One all 3 pieces of the address are known by the proxy, the proxy 26 * can be used to perform normal dbus operations: method-call, get-property 27 * set-property, get-all-properties. 28 * 29 * Addresses are supplied to the object by calling the appropriate method: 30 * service, path, interface. These methods return a _new_ object with the 31 * new information filled in. 32 * 33 * If all pieces are known at compile-time it is suggested to be constructed 34 * similar to the following: 35 * 36 * ``` 37 * constexpr auto systemd = sdbusplus::async::proxy() 38 * .service("org.freedesktop.systemd1") 39 * .path("/org/freedesktop/systemd1") 40 * .interface("org.freedesktop.systemd1.Manager"); 41 * ``` 42 * 43 * The proxy object can be filled as information is available and attempts 44 * to be as efficient as possible (supporting constexpr construction and 45 * using std::string_view mostly). In some cases it is necessary for the 46 * proxy to leave a scope where it would be no longer safe to use the 47 * previously-supplied string_views. The `preserve` operation can be used 48 * to transform an existing proxy into one which is safe to leave (because 49 * it uses less-efficient but safe std::string values). 50 */ 51 template <bool S = false, bool P = false, bool I = false, 52 bool Preserved = false> 53 struct proxy : private sdbusplus::bus::details::bus_friend 54 { 55 // Some typedefs to reduce template noise... 56 57 using string_t = 58 std::conditional_t<Preserved, std::string, std::string_view>; 59 using string_ref = const string_t&; 60 using sv_ref = const std::string_view&; 61 62 template <bool V> 63 using value_t = std::conditional_t<V, string_t, std::monostate>; 64 template <bool V> 65 using value_ref = const value_t<V>&; 66 67 // Default constructor should only work for the "all empty" case. 68 constexpr proxy() 69 requires(!S && !P && !I) 70 = default; 71 constexpr proxy() 72 requires(S || P || I) 73 = delete; 74 75 // Constructor allowing all 3 to be passed in. proxysdbusplus::async::proxy_ns::proxy76 constexpr proxy(value_ref<S> s, value_ref<P> p, value_ref<I> i) : 77 s(s), p(p), i(i) {}; 78 79 // Functions to assign address fields. servicesdbusplus::async::proxy_ns::proxy80 constexpr auto service(string_ref s) const noexcept 81 requires(!S) 82 { 83 return proxy<true, P, I, Preserved>{s, this->p, this->i}; 84 } pathsdbusplus::async::proxy_ns::proxy85 constexpr auto path(string_ref p) const noexcept 86 requires(!P) 87 { 88 return proxy<S, true, I, Preserved>{this->s, p, this->i}; 89 } interfacesdbusplus::async::proxy_ns::proxy90 constexpr auto interface(string_ref i) const noexcept 91 requires(!I) 92 { 93 return proxy<S, P, true, Preserved>{this->s, this->p, i}; 94 } 95 96 /** Make a copyable / returnable proxy. 97 * 98 * Since proxy deals with string_view by default, for efficiency, 99 * there are cases where it would be dangerous for a proxy object to 100 * leave a scope either by a return or a pass into a lambda. This 101 * function will convert an existing proxy into one backed by 102 * `std::string` so that it can safely leave a scope. 103 */ preservesdbusplus::async::proxy_ns::proxy104 auto preserve() const noexcept 105 requires(!Preserved) 106 { 107 using result_t = proxy<S, P, I, true>; 108 return result_t(typename result_t::template value_t<S>(this->s), 109 typename result_t::template value_t<P>(this->p), 110 typename result_t::template value_t<I>(this->i)); 111 } 112 113 /** Perform a method call. 114 * 115 * @tparam Rs - The return type(s) of the method call. 116 * @tparam Ss - The parameter type(s) of the method call. 117 * 118 * @param[in] ctx - The context to use. 119 * @param[in] method - The method name. 120 * @param[in] ss - The calling parameters. 121 * 122 * @return A Sender which completes with either { void, Rs, tuple<Rs...> }. 123 */ 124 template <typename... Rs, typename... Ss> callsdbusplus::async::proxy_ns::proxy125 auto call(context& ctx, sv_ref method, Ss&&... ss) const 126 requires((S) && (P) && (I)) 127 { 128 // Create the method_call message. 129 auto msg = ctx.get_bus().new_method_call(c_str(s), c_str(p), c_str(i), 130 method.data()); 131 if constexpr (sizeof...(Ss) > 0) 132 { 133 msg.append(std::forward<Ss>(ss)...); 134 } 135 136 // Use 'callback' to perform the operation and "then" "unpack" the 137 // contents. 138 return callback([bus = get_busp(ctx), 139 msg = std::move(msg)](auto cb, auto data) mutable { 140 return sd_bus_call_async(bus, nullptr, msg.get(), cb, data, 141 0); 142 }) | 143 execution::then([](message_t&& m) { return m.unpack<Rs...>(); }); 144 } 145 146 /** Get a property. 147 * 148 * @tparam T - The type of the property. 149 * 150 * @param[in] ctx - The context to use. 151 * @param[in] property - The property name. 152 * 153 * @return A Sender which completes with T as the property value. 154 */ 155 template <typename T> get_propertysdbusplus::async::proxy_ns::proxy156 auto get_property(context& ctx, sv_ref property) const 157 requires((S) && (P) && (I)) 158 { 159 using result_t = std::variant<T>; 160 auto prop_intf = proxy(s, p, dbus_prop_intf); 161 162 return prop_intf.template call<result_t>(ctx, "Get", c_str(i), 163 property.data()) | 164 execution::then([](result_t&& v) { return std::get<T>(v); }); 165 } 166 167 /** Get all properties. 168 * 169 * @tparam V - The variant type of all possible properties. 170 * 171 * @param[in] ctx - The context to use. 172 * 173 * @return A Sender which completes with unordered_map<string, V>. 174 */ 175 template <typename V> get_all_propertiessdbusplus::async::proxy_ns::proxy176 auto get_all_properties(context& ctx) const 177 requires((S) && (P) && (I)) 178 { 179 using result_t = std::unordered_map<std::string, V>; 180 auto prop_intf = proxy(s, p, dbus_prop_intf); 181 182 return prop_intf.template call<result_t>(ctx, "GetAll", c_str(i)); 183 } 184 185 /** Set a property. 186 * 187 * @tparam T - The type of the property (usually deduced by the compiler). 188 * 189 * @param[in] ctx - The context to use. 190 * @param[in] property - The property name. 191 * @param[in] value - The value to set. 192 * 193 * @return A Sender which completes void when the property is set. 194 */ 195 template <typename T> set_propertysdbusplus::async::proxy_ns::proxy196 auto set_property(context& ctx, sv_ref property, T&& value) const 197 requires((S) && (P) && (I)) 198 { 199 auto prop_intf = proxy(s, p, dbus_prop_intf); 200 return prop_intf.template call<>( 201 ctx, "Set", c_str(i), property.data(), 202 std::variant<std::decay_t<T>>{std::forward<T>(value)}); 203 } 204 205 private: 206 static constexpr auto dbus_prop_intf = "org.freedesktop.DBus.Properties"; 207 208 // Helper to get the underlying c-string of a string_view or string. c_strsdbusplus::async::proxy_ns::proxy209 static auto c_str(string_ref v) 210 { 211 if constexpr (Preserved) 212 { 213 return v.c_str(); 214 } 215 else 216 { 217 return v.data(); 218 } 219 } 220 221 value_t<S> s = {}; 222 value_t<P> p = {}; 223 value_t<I> i = {}; 224 }; 225 226 } // namespace proxy_ns 227 228 // clang currently has problems with the intersect of default template 229 // parameters and concepts. I've opened llvm/llvm-project#57646 and added 230 // this indirect. 231 using proxy = proxy_ns::proxy<>; 232 233 // Sometimes it is useful to hold onto a proxy, such as in a class member, so 234 // define a type alias for one which can be safely held. 235 using finalized_proxy = proxy_ns::proxy<true, true, true, true>; 236 237 } // namespace sdbusplus::async 238