blob: 286a352f4861da3b6834efb552f5aecbecb7c9a3 [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>
Vernon Mauery261e72b2018-09-25 12:34:25 -070028#include <boost/asio/spawn.hpp>
Patrick Venture95269db2018-08-31 09:19:17 -070029#include <boost/callable_traits.hpp>
Patrick Venture95269db2018-08-31 09:19:17 -070030#include <sdbusplus/asio/detail/async_send_handler.hpp>
James Feist284a0f92018-04-05 15:28:16 -070031#include <sdbusplus/message.hpp>
32#include <sdbusplus/utility/read_into_tuple.hpp>
33#include <sdbusplus/utility/type_traits.hpp>
Patrick Williams127b8ab2020-05-21 15:24:19 -050034
35#include <chrono>
James Feist284a0f92018-04-05 15:28:16 -070036#include <string>
James Feistc14699f2019-06-04 14:11:48 -070037#include <tuple>
James Feist284a0f92018-04-05 15:28:16 -070038
39namespace sdbusplus
40{
41
42namespace asio
43{
44
45/// Root D-Bus IO object
46/**
47 * A connection to a bus, through which messages may be sent or received.
48 */
Patrick Williams0f282c42021-11-19 11:36:18 -060049class connection : public sdbusplus::bus_t
James Feist284a0f92018-04-05 15:28:16 -070050{
51 public:
52 // default to system bus
Ed Tanousc7d104d2019-01-02 14:15:50 -080053 connection(boost::asio::io_context& io) :
Patrick Williams0f282c42021-11-19 11:36:18 -060054 sdbusplus::bus_t(sdbusplus::bus::new_default()), io_(io), socket(io_)
James Feist284a0f92018-04-05 15:28:16 -070055 {
56 socket.assign(get_fd());
James Feistb5755182018-07-16 10:30:08 -070057 read_wait();
James Feist284a0f92018-04-05 15:28:16 -070058 }
Ed Tanousc7d104d2019-01-02 14:15:50 -080059 connection(boost::asio::io_context& io, sd_bus* bus) :
Patrick Williams0f282c42021-11-19 11:36:18 -060060 sdbusplus::bus_t(bus), io_(io), socket(io_)
James Feist284a0f92018-04-05 15:28:16 -070061 {
62 socket.assign(get_fd());
James Feistb5755182018-07-16 10:30:08 -070063 read_wait();
James Feist284a0f92018-04-05 15:28:16 -070064 }
65 ~connection()
66 {
67 // The FD will be closed by the socket object, so assign null to the
68 // sd_bus object to avoid a double close() Ignore return codes here,
69 // because there's nothing we can do about errors
70 socket.release();
71 }
72
Vernon Mauery261e72b2018-09-25 12:34:25 -070073 /** @brief Perform an asynchronous send of a message, executing the handler
74 * upon return and return
75 *
76 * @param[in] m - A message ready to send
Ed Tanous6c974862022-08-31 13:21:32 -070077 * @param[in] token- The completion token to execute upon completion;
Vernon Mauery261e72b2018-09-25 12:34:25 -070078 *
Vernon Mauery261e72b2018-09-25 12:34:25 -070079 */
Ed Tanous6c974862022-08-31 13:21:32 -070080 template <typename CompletionToken>
81 inline auto async_send(message_t& m, CompletionToken&& token,
82 uint64_t timeout = 0)
James Feist284a0f92018-04-05 15:28:16 -070083 {
Ed Tanous6c974862022-08-31 13:21:32 -070084 constexpr bool is_yield =
85 std::is_same_v<CompletionToken, boost::asio::yield_context>;
86 using return_t = std::conditional_t<is_yield, message_t, message_t&>;
87 using callback_t = void(boost::system::error_code, return_t);
88 return boost::asio::async_initiate<CompletionToken, callback_t>(
89 detail::async_send_handler(get(), m, timeout), token);
James Feist284a0f92018-04-05 15:28:16 -070090 }
91
Vernon Mauery261e72b2018-09-25 12:34:25 -070092 /** @brief Perform an asynchronous method call, with input parameter packing
Konrad Sztyberda0b3f12020-03-27 16:48:26 +010093 * and return value unpacking.
Vernon Mauery261e72b2018-09-25 12:34:25 -070094 *
95 * @param[in] handler - A function object that is to be called as a
96 * continuation for the async dbus method call. The
97 * arguments to parse on the return are deduced from
98 * the handler's signature and then passed in along
Patrick Williams10d7aa12021-11-19 11:36:18 -060099 * with an error code and optional message_t
Vernon Mauery261e72b2018-09-25 12:34:25 -0700100 * @param[in] service - The service to call.
101 * @param[in] objpath - The object's path for the call.
102 * @param[in] interf - The object's interface to call.
103 * @param[in] method - The object's method to call.
Konrad Sztyberda0b3f12020-03-27 16:48:26 +0100104 * @param[in] timeout - The timeout for the method call in usec (0 results
105 * in using the default value).
Vernon Mauery261e72b2018-09-25 12:34:25 -0700106 * @param[in] a... - Optional parameters for the method call.
107 *
108 * @return immediate return of the internal handler registration. The
109 * result of the actual asynchronous call will get unpacked from
110 * the message and passed into the handler when the call is
111 * complete.
112 */
James Feist284a0f92018-04-05 15:28:16 -0700113 template <typename MessageHandler, typename... InputArgs>
Konrad Sztyberda0b3f12020-03-27 16:48:26 +0100114 void async_method_call_timed(MessageHandler&& handler,
115 const std::string& service,
116 const std::string& objpath,
117 const std::string& interf,
118 const std::string& method, uint64_t timeout,
119 const InputArgs&... a)
James Feist284a0f92018-04-05 15:28:16 -0700120 {
Vernon Maueryc079db42020-02-06 09:22:01 -0800121 using FunctionTuple = boost::callable_traits::args_t<MessageHandler>;
Patrick Williamsb16ea982021-07-14 10:01:52 -0500122 using FunctionTupleType = utility::decay_tuple_t<FunctionTuple>;
Vernon Maueryc079db42020-02-06 09:22:01 -0800123 constexpr bool returnWithMsg = []() {
Patrick Williams4b646232021-02-22 13:50:48 -0600124 if constexpr ((std::tuple_size_v<FunctionTupleType>) > 1)
Vernon Maueryc079db42020-02-06 09:22:01 -0800125 {
126 return std::is_same_v<
127 std::tuple_element_t<1, FunctionTupleType>,
Patrick Williams10d7aa12021-11-19 11:36:18 -0600128 sdbusplus::message_t>;
Vernon Maueryc079db42020-02-06 09:22:01 -0800129 }
130 return false;
131 }();
Patrick Williamsb16ea982021-07-14 10:01:52 -0500132 using UnpackType = utility::strip_first_n_args_t<returnWithMsg ? 2 : 1,
133 FunctionTupleType>;
Vernon Maueryc079db42020-02-06 09:22:01 -0800134 auto applyHandler = [handler = std::forward<MessageHandler>(handler)](
135 boost::system::error_code ec,
Patrick Williams10d7aa12021-11-19 11:36:18 -0600136 message_t& r) mutable {
James Feist284a0f92018-04-05 15:28:16 -0700137 UnpackType responseData;
138 if (!ec)
139 {
Vernon Maueryc0771902019-05-07 16:53:50 -0700140 try
141 {
142 utility::read_into_tuple(responseData, r);
143 }
Brad Bishop5a6c7902022-09-29 11:05:47 -0400144 catch (const std::exception&)
James Feist284a0f92018-04-05 15:28:16 -0700145 {
146 // Set error code if not already set
147 ec = boost::system::errc::make_error_code(
148 boost::system::errc::invalid_argument);
149 }
150 }
151 // Note. Callback is called whether or not the unpack was
Gunnar Mills31a4b132018-08-14 11:59:13 -0500152 // successful to allow the user to implement their own handling
Richard Marian Thomaiyar42122922019-07-01 21:31:04 +0530153 if constexpr (returnWithMsg)
154 {
155 auto response = std::tuple_cat(std::make_tuple(ec),
156 std::forward_as_tuple(r),
157 std::move(responseData));
158 std::apply(handler, response);
159 }
160 else
161 {
162 auto response = std::tuple_cat(std::make_tuple(ec),
163 std::move(responseData));
164 std::apply(handler, response);
165 }
Vernon Maueryc079db42020-02-06 09:22:01 -0800166 };
Patrick Williams10d7aa12021-11-19 11:36:18 -0600167 message_t m;
Vernon Maueryc079db42020-02-06 09:22:01 -0800168 boost::system::error_code ec;
169 try
170 {
171 m = new_method_call(service.c_str(), objpath.c_str(),
172 interf.c_str(), method.c_str());
173 m.append(a...);
174 }
175 catch (const exception::SdBusError& e)
176 {
177 ec = boost::system::errc::make_error_code(
178 static_cast<boost::system::errc::errc_t>(e.get_errno()));
179 applyHandler(ec, m);
180 return;
181 }
Konrad Sztyberda0b3f12020-03-27 16:48:26 +0100182 async_send(m, std::forward<decltype(applyHandler)>(applyHandler),
183 timeout);
184 }
185
186 /** @brief Perform an asynchronous method call, with input parameter packing
187 * and return value unpacking. Uses the default timeout value.
188 *
189 * @param[in] handler - A function object that is to be called as a
190 * continuation for the async dbus method call. The
191 * arguments to parse on the return are deduced from
192 * the handler's signature and then passed in along
Patrick Williams10d7aa12021-11-19 11:36:18 -0600193 * with an error code and optional message_t
Konrad Sztyberda0b3f12020-03-27 16:48:26 +0100194 * @param[in] service - The service to call.
195 * @param[in] objpath - The object's path for the call.
196 * @param[in] interf - The object's interface to call.
197 * @param[in] method - The object's method to call.
198 * @param[in] a... - Optional parameters for the method call.
199 *
200 * @return immediate return of the internal handler registration. The
201 * result of the actual asynchronous call will get unpacked from
202 * the message and passed into the handler when the call is
203 * complete.
204 */
205 template <typename MessageHandler, typename... InputArgs>
206 void async_method_call(MessageHandler&& handler, const std::string& service,
207 const std::string& objpath,
208 const std::string& interf, const std::string& method,
209 const InputArgs&... a)
210 {
211 async_method_call_timed(std::forward<MessageHandler>(handler), service,
212 objpath, interf, method, 0, a...);
James Feist284a0f92018-04-05 15:28:16 -0700213 }
214
Vernon Mauery261e72b2018-09-25 12:34:25 -0700215 /** @brief Perform a yielding asynchronous method call, with input
216 * parameter packing and return value unpacking
217 *
Vernon Mauery37a5e612019-05-07 16:53:50 -0700218 * @param[in] yield - A yield context to async block upon.
219 * @param[in] ec - an error code that will be set for any errors
Vernon Mauery261e72b2018-09-25 12:34:25 -0700220 * @param[in] service - The service to call.
221 * @param[in] objpath - The object's path for the call.
222 * @param[in] interf - The object's interface to call.
223 * @param[in] method - The object's method to call.
224 * @param[in] a... - Optional parameters for the method call.
225 *
226 * @return Unpacked value of RetType
227 */
228 template <typename... RetTypes, typename... InputArgs>
229 auto yield_method_call(boost::asio::yield_context yield,
Vernon Mauery37a5e612019-05-07 16:53:50 -0700230 boost::system::error_code& ec,
Vernon Mauery261e72b2018-09-25 12:34:25 -0700231 const std::string& service,
232 const std::string& objpath,
233 const std::string& interf, const std::string& method,
234 const InputArgs&... a)
235 {
Patrick Williams10d7aa12021-11-19 11:36:18 -0600236 message_t m;
Vernon Maueryc079db42020-02-06 09:22:01 -0800237 try
238 {
239 m = new_method_call(service.c_str(), objpath.c_str(),
240 interf.c_str(), method.c_str());
241 m.append(a...);
242 }
243 catch (const exception::SdBusError& e)
244 {
245 ec = boost::system::errc::make_error_code(
246 static_cast<boost::system::errc::errc_t>(e.get_errno()));
247 }
Patrick Williams10d7aa12021-11-19 11:36:18 -0600248 message_t r;
Vernon Maueryc079db42020-02-06 09:22:01 -0800249 if (!ec)
250 {
251 r = async_send(m, yield[ec]);
252 }
Vernon Mauery261e72b2018-09-25 12:34:25 -0700253 if constexpr (sizeof...(RetTypes) == 0)
254 {
255 // void return
256 return;
257 }
258 else if constexpr (sizeof...(RetTypes) == 1)
259 {
Patrick Williams64f01222021-07-14 10:14:51 -0500260 if constexpr (std::is_same_v<utility::first_type_t<RetTypes...>,
261 void>)
Vernon Mauery261e72b2018-09-25 12:34:25 -0700262 {
263 return;
264 }
265 else
266 {
267 // single item return
Patrick Williamsb16ea982021-07-14 10:01:52 -0500268 utility::first_type_t<RetTypes...> responseData{};
Vernon Mauery37a5e612019-05-07 16:53:50 -0700269 // before attempting to read, check ec and bail on error
270 if (ec)
271 {
272 return responseData;
273 }
274 try
275 {
276 r.read(responseData);
277 }
Brad Bishop5a6c7902022-09-29 11:05:47 -0400278 catch (const std::exception&)
Vernon Mauery37a5e612019-05-07 16:53:50 -0700279 {
280 ec = boost::system::errc::make_error_code(
281 boost::system::errc::invalid_argument);
282 // responseData will be default-constructed...
283 }
Vernon Mauery261e72b2018-09-25 12:34:25 -0700284 return responseData;
285 }
286 }
287 else
288 {
289 // tuple of things to return
Patrick Williams5d4e4b22020-05-20 21:39:22 -0500290 std::tuple<RetTypes...> responseData{};
Vernon Mauery37a5e612019-05-07 16:53:50 -0700291 // before attempting to read, check ec and bail on error
292 if (ec)
293 {
294 return responseData;
295 }
296 try
297 {
298 r.read(responseData);
299 }
Brad Bishop5a6c7902022-09-29 11:05:47 -0400300 catch (const std::exception&)
Vernon Mauery37a5e612019-05-07 16:53:50 -0700301 {
302 ec = boost::system::errc::make_error_code(
303 boost::system::errc::invalid_argument);
304 // responseData will be default-constructed...
305 }
Vernon Mauery261e72b2018-09-25 12:34:25 -0700306 return responseData;
307 }
308 }
309
Ed Tanousc7d104d2019-01-02 14:15:50 -0800310 boost::asio::io_context& get_io_context()
Vernon Mauery076d14a2018-10-02 15:10:20 -0700311 {
312 return io_;
313 }
314
James Feist284a0f92018-04-05 15:28:16 -0700315 private:
Ed Tanousc7d104d2019-01-02 14:15:50 -0800316 boost::asio::io_context& io_;
James Feist284a0f92018-04-05 15:28:16 -0700317 boost::asio::posix::stream_descriptor socket;
318
James Feistb5755182018-07-16 10:30:08 -0700319 void read_wait()
James Feist284a0f92018-04-05 15:28:16 -0700320 {
321 socket.async_read_some(
322 boost::asio::null_buffers(),
Vernon Mauery0c765462020-12-15 14:36:34 -0800323 [&](const boost::system::error_code& ec, std::size_t) {
324 if (ec)
325 {
326 return;
327 }
James Feistb5755182018-07-16 10:30:08 -0700328 if (process_discard())
329 {
330 read_immediate();
331 }
332 else
333 {
334 read_wait();
335 }
James Feist284a0f92018-04-05 15:28:16 -0700336 });
337 }
James Feistb5755182018-07-16 10:30:08 -0700338 void read_immediate()
339 {
Ed Tanousc7d104d2019-01-02 14:15:50 -0800340 boost::asio::post(io_, [&] {
James Feistb5755182018-07-16 10:30:08 -0700341 if (process_discard())
342 {
343 read_immediate();
344 }
345 else
346 {
347 read_wait();
348 }
349 });
350 }
James Feist284a0f92018-04-05 15:28:16 -0700351};
352
353} // namespace asio
354
355} // namespace sdbusplus