blob: 841c6d76d11a52e57d4b3250f29b79c8fe3ee38b [file] [log] [blame]
James Feist284a0f92018-04-05 15:28:16 -07001/*
2// Copyright (c) 2018 Intel 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#pragma once
17
Vernon Mauery076d14a2018-10-02 15:10:20 -070018#ifndef BOOST_COROUTINES_NO_DEPRECATION_WARNING
19// users should define this if they directly include boost/asio/spawn.hpp,
20// but by defining it here, warnings won't cause problems with a compile
21#define BOOST_COROUTINES_NO_DEPRECATION_WARNING
22#endif
23
Ed Tanous38ab5ec2020-08-17 17:08:20 -070024#include <boost/asio/async_result.hpp>
25#include <boost/asio/io_context.hpp>
26#include <boost/asio/posix/stream_descriptor.hpp>
27#include <boost/asio/post.hpp>
Ed Tanous1778b122021-09-13 16:37:40 -070028#ifndef SDBUSPLUS_DISABLE_BOOST_COROUTINES
Vernon Mauery261e72b2018-09-25 12:34:25 -070029#include <boost/asio/spawn.hpp>
Ed Tanous1778b122021-09-13 16:37:40 -070030#endif
Patrick Venture95269db2018-08-31 09:19:17 -070031#include <boost/callable_traits.hpp>
Patrick Venture95269db2018-08-31 09:19:17 -070032#include <sdbusplus/asio/detail/async_send_handler.hpp>
James Feist284a0f92018-04-05 15:28:16 -070033#include <sdbusplus/message.hpp>
34#include <sdbusplus/utility/read_into_tuple.hpp>
35#include <sdbusplus/utility/type_traits.hpp>
Patrick Williams127b8ab2020-05-21 15:24:19 -050036
37#include <chrono>
James Feist284a0f92018-04-05 15:28:16 -070038#include <string>
James Feistc14699f2019-06-04 14:11:48 -070039#include <tuple>
James Feist284a0f92018-04-05 15:28:16 -070040
41namespace sdbusplus
42{
43
44namespace asio
45{
46
47/// Root D-Bus IO object
48/**
49 * A connection to a bus, through which messages may be sent or received.
50 */
Patrick Williams0f282c42021-11-19 11:36:18 -060051class connection : public sdbusplus::bus_t
James Feist284a0f92018-04-05 15:28:16 -070052{
53 public:
54 // default to system bus
Ed Tanousc7d104d2019-01-02 14:15:50 -080055 connection(boost::asio::io_context& io) :
Patrick Williamsfdfd3af2023-05-11 14:44:03 -050056 sdbusplus::bus_t(sdbusplus::bus::new_default()), io_(io),
57 socket(io_.get_executor(), get_fd())
James Feist284a0f92018-04-05 15:28:16 -070058 {
Rahul Kapoor894d6ca2023-11-21 20:05:27 +000059 read_immediate();
James Feist284a0f92018-04-05 15:28:16 -070060 }
Ed Tanousc7d104d2019-01-02 14:15:50 -080061 connection(boost::asio::io_context& io, sd_bus* bus) :
Patrick Williamsfdfd3af2023-05-11 14:44:03 -050062 sdbusplus::bus_t(bus), io_(io), socket(io_.get_executor(), get_fd())
James Feist284a0f92018-04-05 15:28:16 -070063 {
Rahul Kapoor894d6ca2023-11-21 20:05:27 +000064 read_immediate();
James Feist284a0f92018-04-05 15:28:16 -070065 }
66 ~connection()
67 {
68 // The FD will be closed by the socket object, so assign null to the
69 // sd_bus object to avoid a double close() Ignore return codes here,
70 // because there's nothing we can do about errors
71 socket.release();
72 }
73
Vernon Mauery261e72b2018-09-25 12:34:25 -070074 /** @brief Perform an asynchronous send of a message, executing the handler
75 * upon return and return
76 *
77 * @param[in] m - A message ready to send
Ed Tanousddc57bd2023-01-05 10:47:32 -080078 * @param[in] token - The completion token to execute upon completion;
79 * @param[in] timeout - The timeout in microseconds
Vernon Mauery261e72b2018-09-25 12:34:25 -070080 *
Vernon Mauery261e72b2018-09-25 12:34:25 -070081 */
Ed Tanous6c974862022-08-31 13:21:32 -070082 template <typename CompletionToken>
83 inline auto async_send(message_t& m, CompletionToken&& token,
84 uint64_t timeout = 0)
James Feist284a0f92018-04-05 15:28:16 -070085 {
Ed Tanous3cc9c302023-05-30 12:57:31 -070086#ifdef SDBUSPLUS_DISABLE_BOOST_COROUTINES
87 constexpr bool is_yield = false;
88#else
Ed Tanous6c974862022-08-31 13:21:32 -070089 constexpr bool is_yield =
90 std::is_same_v<CompletionToken, boost::asio::yield_context>;
Ed Tanous3cc9c302023-05-30 12:57:31 -070091#endif
Ed Tanous6c974862022-08-31 13:21:32 -070092 using return_t = std::conditional_t<is_yield, message_t, message_t&>;
93 using callback_t = void(boost::system::error_code, return_t);
94 return boost::asio::async_initiate<CompletionToken, callback_t>(
95 detail::async_send_handler(get(), m, timeout), token);
James Feist284a0f92018-04-05 15:28:16 -070096 }
97
Vernon Mauery261e72b2018-09-25 12:34:25 -070098 /** @brief Perform an asynchronous method call, with input parameter packing
Konrad Sztyberda0b3f12020-03-27 16:48:26 +010099 * and return value unpacking.
Vernon Mauery261e72b2018-09-25 12:34:25 -0700100 *
101 * @param[in] handler - A function object that is to be called as a
102 * continuation for the async dbus method call. The
103 * arguments to parse on the return are deduced from
104 * the handler's signature and then passed in along
Patrick Williams10d7aa12021-11-19 11:36:18 -0600105 * with an error code and optional message_t
Vernon Mauery261e72b2018-09-25 12:34:25 -0700106 * @param[in] service - The service to call.
107 * @param[in] objpath - The object's path for the call.
108 * @param[in] interf - The object's interface to call.
109 * @param[in] method - The object's method to call.
Konrad Sztyberda0b3f12020-03-27 16:48:26 +0100110 * @param[in] timeout - The timeout for the method call in usec (0 results
111 * in using the default value).
Ed Tanousddc57bd2023-01-05 10:47:32 -0800112 * @param[in] a - Optional parameters for the method call.
Vernon Mauery261e72b2018-09-25 12:34:25 -0700113 *
Vernon Mauery261e72b2018-09-25 12:34:25 -0700114 */
James Feist284a0f92018-04-05 15:28:16 -0700115 template <typename MessageHandler, typename... InputArgs>
Konrad Sztyberda0b3f12020-03-27 16:48:26 +0100116 void async_method_call_timed(MessageHandler&& handler,
117 const std::string& service,
118 const std::string& objpath,
119 const std::string& interf,
120 const std::string& method, uint64_t timeout,
121 const InputArgs&... a)
James Feist284a0f92018-04-05 15:28:16 -0700122 {
Vernon Maueryc079db42020-02-06 09:22:01 -0800123 using FunctionTuple = boost::callable_traits::args_t<MessageHandler>;
Patrick Williamsb16ea982021-07-14 10:01:52 -0500124 using FunctionTupleType = utility::decay_tuple_t<FunctionTuple>;
Vernon Maueryc079db42020-02-06 09:22:01 -0800125 constexpr bool returnWithMsg = []() {
Patrick Williams4b646232021-02-22 13:50:48 -0600126 if constexpr ((std::tuple_size_v<FunctionTupleType>) > 1)
Vernon Maueryc079db42020-02-06 09:22:01 -0800127 {
128 return std::is_same_v<
129 std::tuple_element_t<1, FunctionTupleType>,
Patrick Williams10d7aa12021-11-19 11:36:18 -0600130 sdbusplus::message_t>;
Vernon Maueryc079db42020-02-06 09:22:01 -0800131 }
132 return false;
133 }();
Patrick Williamsb16ea982021-07-14 10:01:52 -0500134 using UnpackType = utility::strip_first_n_args_t<returnWithMsg ? 2 : 1,
135 FunctionTupleType>;
Patrick Williams6db88382023-10-20 11:18:17 -0500136 auto applyHandler = [handler = std::forward<MessageHandler>(handler)](
137 boost::system::error_code ec,
138 message_t& r) mutable {
James Feist284a0f92018-04-05 15:28:16 -0700139 UnpackType responseData;
140 if (!ec)
141 {
Vernon Maueryc0771902019-05-07 16:53:50 -0700142 try
143 {
144 utility::read_into_tuple(responseData, r);
145 }
Brad Bishop5a6c7902022-09-29 11:05:47 -0400146 catch (const std::exception&)
James Feist284a0f92018-04-05 15:28:16 -0700147 {
148 // Set error code if not already set
149 ec = boost::system::errc::make_error_code(
150 boost::system::errc::invalid_argument);
151 }
152 }
153 // Note. Callback is called whether or not the unpack was
Gunnar Mills31a4b132018-08-14 11:59:13 -0500154 // successful to allow the user to implement their own handling
Richard Marian Thomaiyar42122922019-07-01 21:31:04 +0530155 if constexpr (returnWithMsg)
156 {
157 auto response = std::tuple_cat(std::make_tuple(ec),
158 std::forward_as_tuple(r),
159 std::move(responseData));
160 std::apply(handler, response);
161 }
162 else
163 {
164 auto response = std::tuple_cat(std::make_tuple(ec),
165 std::move(responseData));
166 std::apply(handler, response);
167 }
Vernon Maueryc079db42020-02-06 09:22:01 -0800168 };
Patrick Williams10d7aa12021-11-19 11:36:18 -0600169 message_t m;
Vernon Maueryc079db42020-02-06 09:22:01 -0800170 boost::system::error_code ec;
171 try
172 {
173 m = new_method_call(service.c_str(), objpath.c_str(),
174 interf.c_str(), method.c_str());
175 m.append(a...);
176 }
177 catch (const exception::SdBusError& e)
178 {
179 ec = boost::system::errc::make_error_code(
180 static_cast<boost::system::errc::errc_t>(e.get_errno()));
181 applyHandler(ec, m);
182 return;
183 }
Konrad Sztyberda0b3f12020-03-27 16:48:26 +0100184 async_send(m, std::forward<decltype(applyHandler)>(applyHandler),
185 timeout);
186 }
187
188 /** @brief Perform an asynchronous method call, with input parameter packing
189 * and return value unpacking. Uses the default timeout value.
190 *
191 * @param[in] handler - A function object that is to be called as a
192 * continuation for the async dbus method call. The
193 * arguments to parse on the return are deduced from
194 * the handler's signature and then passed in along
Patrick Williams10d7aa12021-11-19 11:36:18 -0600195 * with an error code and optional message_t
Konrad Sztyberda0b3f12020-03-27 16:48:26 +0100196 * @param[in] service - The service to call.
197 * @param[in] objpath - The object's path for the call.
198 * @param[in] interf - The object's interface to call.
199 * @param[in] method - The object's method to call.
Ed Tanousddc57bd2023-01-05 10:47:32 -0800200 * @param[in] a - Optional parameters for the method call.
Konrad Sztyberda0b3f12020-03-27 16:48:26 +0100201 *
Konrad Sztyberda0b3f12020-03-27 16:48:26 +0100202 */
203 template <typename MessageHandler, typename... InputArgs>
204 void async_method_call(MessageHandler&& handler, const std::string& service,
205 const std::string& objpath,
206 const std::string& interf, const std::string& method,
207 const InputArgs&... a)
208 {
209 async_method_call_timed(std::forward<MessageHandler>(handler), service,
210 objpath, interf, method, 0, a...);
James Feist284a0f92018-04-05 15:28:16 -0700211 }
212
Ed Tanous1778b122021-09-13 16:37:40 -0700213#ifndef SDBUSPLUS_DISABLE_BOOST_COROUTINES
Vernon Mauery261e72b2018-09-25 12:34:25 -0700214 /** @brief Perform a yielding asynchronous method call, with input
215 * parameter packing and return value unpacking
216 *
Vernon Mauery37a5e612019-05-07 16:53:50 -0700217 * @param[in] yield - A yield context to async block upon.
218 * @param[in] ec - an error code that will be set for any errors
Vernon Mauery261e72b2018-09-25 12:34:25 -0700219 * @param[in] service - The service to call.
220 * @param[in] objpath - The object's path for the call.
221 * @param[in] interf - The object's interface to call.
222 * @param[in] method - The object's method to call.
Ed Tanousddc57bd2023-01-05 10:47:32 -0800223 * @param[in] a - Optional parameters for the method call.
Vernon Mauery261e72b2018-09-25 12:34:25 -0700224 *
225 * @return Unpacked value of RetType
226 */
227 template <typename... RetTypes, typename... InputArgs>
228 auto yield_method_call(boost::asio::yield_context yield,
Vernon Mauery37a5e612019-05-07 16:53:50 -0700229 boost::system::error_code& ec,
Vernon Mauery261e72b2018-09-25 12:34:25 -0700230 const std::string& service,
231 const std::string& objpath,
232 const std::string& interf, const std::string& method,
233 const InputArgs&... a)
234 {
Patrick Williams10d7aa12021-11-19 11:36:18 -0600235 message_t m;
Vernon Maueryc079db42020-02-06 09:22:01 -0800236 try
237 {
238 m = new_method_call(service.c_str(), objpath.c_str(),
239 interf.c_str(), method.c_str());
240 m.append(a...);
241 }
242 catch (const exception::SdBusError& e)
243 {
244 ec = boost::system::errc::make_error_code(
245 static_cast<boost::system::errc::errc_t>(e.get_errno()));
246 }
Patrick Williams10d7aa12021-11-19 11:36:18 -0600247 message_t r;
Vernon Maueryc079db42020-02-06 09:22:01 -0800248 if (!ec)
249 {
250 r = async_send(m, yield[ec]);
251 }
Vernon Mauery261e72b2018-09-25 12:34:25 -0700252 if constexpr (sizeof...(RetTypes) == 0)
253 {
254 // void return
255 return;
256 }
257 else if constexpr (sizeof...(RetTypes) == 1)
258 {
Patrick Williams64f01222021-07-14 10:14:51 -0500259 if constexpr (std::is_same_v<utility::first_type_t<RetTypes...>,
260 void>)
Vernon Mauery261e72b2018-09-25 12:34:25 -0700261 {
262 return;
263 }
264 else
265 {
266 // single item return
Patrick Williamsb16ea982021-07-14 10:01:52 -0500267 utility::first_type_t<RetTypes...> responseData{};
Vernon Mauery37a5e612019-05-07 16:53:50 -0700268 // before attempting to read, check ec and bail on error
269 if (ec)
270 {
271 return responseData;
272 }
273 try
274 {
275 r.read(responseData);
276 }
Brad Bishop5a6c7902022-09-29 11:05:47 -0400277 catch (const std::exception&)
Vernon Mauery37a5e612019-05-07 16:53:50 -0700278 {
279 ec = boost::system::errc::make_error_code(
280 boost::system::errc::invalid_argument);
281 // responseData will be default-constructed...
282 }
Vernon Mauery261e72b2018-09-25 12:34:25 -0700283 return responseData;
284 }
285 }
286 else
287 {
288 // tuple of things to return
Patrick Williams5d4e4b22020-05-20 21:39:22 -0500289 std::tuple<RetTypes...> responseData{};
Vernon Mauery37a5e612019-05-07 16:53:50 -0700290 // before attempting to read, check ec and bail on error
291 if (ec)
292 {
293 return responseData;
294 }
295 try
296 {
297 r.read(responseData);
298 }
Brad Bishop5a6c7902022-09-29 11:05:47 -0400299 catch (const std::exception&)
Vernon Mauery37a5e612019-05-07 16:53:50 -0700300 {
301 ec = boost::system::errc::make_error_code(
302 boost::system::errc::invalid_argument);
303 // responseData will be default-constructed...
304 }
Vernon Mauery261e72b2018-09-25 12:34:25 -0700305 return responseData;
306 }
307 }
Ed Tanous1778b122021-09-13 16:37:40 -0700308#endif
Ed Tanousc7d104d2019-01-02 14:15:50 -0800309 boost::asio::io_context& get_io_context()
Vernon Mauery076d14a2018-10-02 15:10:20 -0700310 {
311 return io_;
312 }
313
James Feist284a0f92018-04-05 15:28:16 -0700314 private:
Ed Tanousc7d104d2019-01-02 14:15:50 -0800315 boost::asio::io_context& io_;
James Feist284a0f92018-04-05 15:28:16 -0700316 boost::asio::posix::stream_descriptor socket;
317
James Feistb5755182018-07-16 10:30:08 -0700318 void read_wait()
James Feist284a0f92018-04-05 15:28:16 -0700319 {
320 socket.async_read_some(
321 boost::asio::null_buffers(),
Vernon Mauery0c765462020-12-15 14:36:34 -0800322 [&](const boost::system::error_code& ec, std::size_t) {
Patrick Williamsd2149042023-05-10 07:50:13 -0500323 if (ec)
324 {
325 return;
326 }
327 if (process_discard())
328 {
329 read_immediate();
330 }
331 else
332 {
333 read_wait();
334 }
Patrick Williams6db88382023-10-20 11:18:17 -0500335 });
James Feist284a0f92018-04-05 15:28:16 -0700336 }
James Feistb5755182018-07-16 10:30:08 -0700337 void read_immediate()
338 {
Ed Tanousc7d104d2019-01-02 14:15:50 -0800339 boost::asio::post(io_, [&] {
James Feistb5755182018-07-16 10:30:08 -0700340 if (process_discard())
341 {
342 read_immediate();
343 }
344 else
345 {
346 read_wait();
347 }
348 });
349 }
James Feist284a0f92018-04-05 15:28:16 -0700350};
351
352} // namespace asio
353
354} // namespace sdbusplus