1 #pragma once 2 3 #include <systemd/sd-bus.h> 4 5 #include <sdbusplus/exception.hpp> 6 #include <sdbusplus/message/types.hpp> 7 #include <sdbusplus/utility/tuple_to_array.hpp> 8 #include <sdbusplus/utility/type_traits.hpp> 9 10 #include <string> 11 #include <tuple> 12 #include <type_traits> 13 #include <utility> 14 #include <variant> 15 16 namespace sdbusplus 17 { 18 19 namespace message 20 { 21 22 /** @brief Read data from an sdbus message. 23 * 24 * (This is an empty no-op function that is useful in some cases for 25 * variadic template reasons.) 26 */ 27 inline void read(sdbusplus::SdBusInterface* /*intf*/, sd_bus_message* /*m*/) {} 28 /** @brief Read data from an sdbus message. 29 * 30 * @param[in] m - The message to read from. 31 * @tparam Args - C++ types of arguments to read from message. 32 * @param[out] args - References to place contents read from message. 33 * 34 * This function will, at compile-time, deduce the DBus types of the passed 35 * C++ values and call the sd_bus_message_read functions with the 36 * appropriate type parameters. It may also do conversions, where needed, 37 * to convert C++ types into C representations (eg. string, vector). 38 */ 39 template <typename... Args> 40 void read(sdbusplus::SdBusInterface* intf, sd_bus_message* m, Args&&... args); 41 42 namespace details 43 { 44 45 /** @brief Utility to identify C++ types that may not be grouped into a 46 * single sd_bus_message_read call and instead need special 47 * handling. 48 * 49 * @tparam T - Type for identification. 50 * 51 * User-defined types are expected to inherit from std::false_type. 52 * Enums are converted from strings, so must be done one at a time. 53 * 54 */ 55 template <typename T, typename Enable = void> 56 struct can_read_multiple : 57 std::conditional_t<std::is_enum_v<T>, std::false_type, std::true_type> 58 {}; 59 // unix_fd's int needs to be wrapped 60 template <> 61 struct can_read_multiple<unix_fd> : std::false_type 62 {}; 63 // std::string needs a char* conversion. 64 template <> 65 struct can_read_multiple<std::string> : std::false_type 66 {}; 67 // object_path needs a char* conversion. 68 template <> 69 struct can_read_multiple<object_path> : std::false_type 70 {}; 71 // signature needs a char* conversion. 72 template <> 73 struct can_read_multiple<signature> : std::false_type 74 {}; 75 // bool needs to be resized to int, per sdbus documentation. 76 template <> 77 struct can_read_multiple<bool> : std::false_type 78 {}; 79 80 // std::vector/map/unordered_vector/set need loops 81 template <typename T> 82 struct can_read_multiple< 83 T, typename std::enable_if_t<utility::has_emplace_method_v<T> || 84 utility::has_emplace_back_method_v<T>>> : 85 std::false_type 86 {}; 87 88 // std::pair needs to be broken down into components. 89 template <typename T1, typename T2> 90 struct can_read_multiple<std::pair<T1, T2>> : std::false_type 91 {}; 92 93 // std::tuple needs to be broken down into components. 94 template <typename... Args> 95 struct can_read_multiple<std::tuple<Args...>> : std::false_type 96 {}; 97 // variant needs to be broken down into components. 98 template <typename... Args> 99 struct can_read_multiple<std::variant<Args...>> : std::false_type 100 {}; 101 102 template <typename... Args> 103 inline constexpr bool can_read_multiple_v = can_read_multiple<Args...>::value; 104 105 /** @brief Utility to read a single C++ element from a sd_bus_message. 106 * 107 * User-defined types are expected to specialize this template in order to 108 * get their functionality. 109 * 110 * @tparam S - Type of element to read. 111 */ 112 template <typename S> 113 struct read_single 114 { 115 // Downcast 116 template <typename T> 117 using Td = types::details::type_id_downcast_t<T>; 118 119 /** @brief Do the operation to read element. 120 * 121 * @tparam T - Type of element to read. 122 * 123 * Template parameters T (function) and S (class) are different 124 * to allow the function to be utilized for many varients of S: 125 * S&, S&&, const S&, volatile S&, etc. The type_id_downcast is used 126 * to ensure T and S are equivalent. For 'char*', this also allows 127 * use for 'char[N]' types. 128 * 129 * @param[in] m - sd_bus_message to read from. 130 * @param[out] t - The reference to read item into. 131 */ 132 template <typename T> 133 static void op(sdbusplus::SdBusInterface* intf, sd_bus_message* m, T&& t) 134 requires(!std::is_enum_v<Td<T>>) 135 { 136 // For this default implementation, we need to ensure that only 137 // basic types are used. 138 static_assert(std::is_fundamental_v<Td<T>> || 139 std::is_convertible_v<Td<T>, const char*> || 140 std::is_convertible_v<Td<T>, details::unix_fd_type>, 141 "Non-basic types are not allowed."); 142 143 constexpr auto dbusType = std::get<0>(types::type_id<T>()); 144 int r = intf->sd_bus_message_read_basic(m, dbusType, &t); 145 if (r < 0) 146 { 147 throw exception::SdBusError( 148 -r, "sd_bus_message_read_basic fundamental"); 149 } 150 } 151 152 template <typename T> 153 static void op(sdbusplus::SdBusInterface* intf, sd_bus_message* m, T&& t) 154 requires(std::is_enum_v<Td<T>>) 155 { 156 std::string value{}; 157 sdbusplus::message::read(intf, m, value); 158 159 auto r = sdbusplus::message::convert_from_string<Td<T>>(value); 160 if (!r) 161 { 162 throw sdbusplus::exception::InvalidEnumString(); 163 } 164 t = *r; 165 } 166 }; 167 168 template <typename T> 169 using read_single_t = read_single<types::details::type_id_downcast_t<T>>; 170 171 /** @brief Specialization of read_single for various string class types. 172 * 173 * Supports std::strings, details::string_wrapper and 174 * details::string_path_wrapper. 175 */ 176 template <typename S> 177 requires(std::is_same_v<S, std::string> || 178 std::is_same_v<S, details::string_wrapper> || 179 std::is_same_v<S, details::string_path_wrapper>) 180 struct read_single<S> 181 { 182 template <typename T> 183 static void op(sdbusplus::SdBusInterface* intf, sd_bus_message* m, T&& t) 184 { 185 constexpr auto dbusType = std::get<0>(types::type_id<T>()); 186 const char* str = nullptr; 187 int r = intf->sd_bus_message_read_basic(m, dbusType, &str); 188 if (r < 0) 189 { 190 throw exception::SdBusError(-r, "sd_bus_message_read_basic string"); 191 } 192 t = S(str); 193 } 194 }; 195 196 /** @brief Specialization of read_single for bools. */ 197 template <typename S> 198 requires(std::is_same_v<S, bool>) 199 struct read_single<S> 200 { 201 template <typename T> 202 static void op(sdbusplus::SdBusInterface* intf, sd_bus_message* m, T&& t) 203 { 204 constexpr auto dbusType = std::get<0>(types::type_id<T>()); 205 int i = 0; 206 int r = intf->sd_bus_message_read_basic(m, dbusType, &i); 207 if (r < 0) 208 { 209 throw exception::SdBusError(-r, "sd_bus_message_read_basic bool"); 210 } 211 t = (i != 0); 212 } 213 }; 214 215 /** @brief Specialization of read_single for std::vectors. */ 216 template <typename S> 217 requires(utility::has_emplace_back_method_v<S>) 218 struct read_single<S> 219 { 220 template <typename T> 221 static void op(sdbusplus::SdBusInterface* intf, sd_bus_message* m, T&& t) 222 { 223 constexpr auto dbusType = utility::tuple_to_array(types::type_id<S>()); 224 int r = intf->sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, 225 dbusType.data() + 1); 226 if (r < 0) 227 { 228 throw exception::SdBusError( 229 -r, "sd_bus_message_enter_container emplace_back_container"); 230 } 231 232 while (!(r = intf->sd_bus_message_at_end(m, false))) 233 { 234 types::details::type_id_downcast_t<typename S::value_type> s; 235 sdbusplus::message::read(intf, m, s); 236 t.emplace_back(std::move(s)); 237 } 238 if (r < 0) 239 { 240 throw exception::SdBusError( 241 -r, "sd_bus_message_at_end emplace_back_container"); 242 } 243 244 r = intf->sd_bus_message_exit_container(m); 245 if (r < 0) 246 { 247 throw exception::SdBusError( 248 -r, "sd_bus_message_exit_container emplace_back_container"); 249 } 250 } 251 }; 252 253 /** @brief Specialization of read_single for std::map. */ 254 template <typename S> 255 requires(utility::has_emplace_method_v<S>) 256 struct read_single<S> 257 { 258 template <typename T> 259 static void op(sdbusplus::SdBusInterface* intf, sd_bus_message* m, T&& t) 260 { 261 constexpr auto dbusType = utility::tuple_to_array(types::type_id<S>()); 262 int r = intf->sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, 263 dbusType.data() + 1); 264 if (r < 0) 265 { 266 throw exception::SdBusError( 267 -r, "sd_bus_message_enter_container emplace_container"); 268 } 269 270 while (!(r = intf->sd_bus_message_at_end(m, false))) 271 { 272 types::details::type_id_downcast_t<typename S::value_type> s; 273 sdbusplus::message::read(intf, m, s); 274 t.emplace(std::move(s)); 275 } 276 if (r < 0) 277 { 278 throw exception::SdBusError( 279 -r, "sd_bus_message_at_end emplace_container"); 280 } 281 282 r = intf->sd_bus_message_exit_container(m); 283 if (r < 0) 284 { 285 throw exception::SdBusError( 286 -r, "sd_bus_message_exit_container emplace_container"); 287 } 288 } 289 }; 290 291 /** @brief Specialization of read_single for std::tuples and std::pairs. */ 292 template <typename S> 293 requires requires(S& s) { std::get<0>(s); } 294 struct read_single<S> 295 { 296 template <typename T> 297 static void op(sdbusplus::SdBusInterface* intf, sd_bus_message* m, T&& t) 298 { 299 constexpr auto dbusType = 300 utility::tuple_to_array(types::type_id_tuple<S>()); 301 302 // Tuples use TYPE_STRUCT, pair uses TYPE_DICT_ENTRY. 303 // Use the presence of `t.first` to determine if it is a pair. 304 constexpr auto tupleType = [&]() { 305 if constexpr (requires { t.first; }) 306 { 307 return SD_BUS_TYPE_DICT_ENTRY; 308 } 309 return SD_BUS_TYPE_STRUCT; 310 }(); 311 312 int r = intf->sd_bus_message_enter_container(m, tupleType, 313 dbusType.data()); 314 if (r < 0) 315 { 316 throw exception::SdBusError(-r, 317 "sd_bus_message_enter_container tuple"); 318 } 319 320 std::apply( 321 [&](auto&... args) { sdbusplus::message::read(intf, m, args...); }, 322 t); 323 324 r = intf->sd_bus_message_exit_container(m); 325 if (r < 0) 326 { 327 throw exception::SdBusError(-r, 328 "sd_bus_message_exit_container tuple"); 329 } 330 } 331 }; 332 333 /** @brief Specialization of read_single for std::variant. */ 334 template <typename... Args> 335 struct read_single<std::variant<Args...>> 336 { 337 // Downcast 338 template <typename T> 339 using Td = types::details::type_id_downcast_t<T>; 340 341 template <typename T, typename T1, typename... Args1> 342 static void read(sdbusplus::SdBusInterface* intf, sd_bus_message* m, T&& t) 343 { 344 constexpr auto dbusType = utility::tuple_to_array(types::type_id<T1>()); 345 346 int r = intf->sd_bus_message_verify_type(m, SD_BUS_TYPE_VARIANT, 347 dbusType.data()); 348 if (r < 0) 349 { 350 throw exception::SdBusError(-r, 351 "sd_bus_message_verify_type variant"); 352 } 353 if (!r) 354 { 355 if constexpr (sizeof...(Args1) == 0) 356 { 357 r = intf->sd_bus_message_skip(m, "v"); 358 if (r < 0) 359 { 360 throw exception::SdBusError(-r, 361 "sd_bus_message_skip variant"); 362 } 363 t = std::remove_reference_t<T>{}; 364 } 365 else 366 { 367 read<T, Args1...>(intf, m, t); 368 } 369 return; 370 } 371 372 r = intf->sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, 373 dbusType.data()); 374 if (r < 0) 375 { 376 throw exception::SdBusError( 377 -r, "sd_bus_message_enter_container variant"); 378 } 379 380 // If this type is an enum or string, we don't know which is the 381 // valid parsing. Delegate to 'convert_from_string' so we do the 382 // correct conversion. 383 if constexpr (std::is_enum_v<Td<T1>> || 384 std::is_same_v<std::string, Td<T1>>) 385 { 386 std::string str{}; 387 sdbusplus::message::read(intf, m, str); 388 auto ret = 389 sdbusplus::message::convert_from_string<std::variant<Args...>>( 390 str); 391 392 if (!ret) 393 { 394 throw sdbusplus::exception::InvalidEnumString(); 395 } 396 397 t = std::move(*ret); 398 } 399 else // otherise, read it out directly. 400 { 401 std::remove_reference_t<T1> t1; 402 sdbusplus::message::read(intf, m, t1); 403 t = std::move(t1); 404 } 405 406 r = intf->sd_bus_message_exit_container(m); 407 if (r < 0) 408 { 409 throw exception::SdBusError( 410 -r, "sd_bus_message_exit_container variant"); 411 } 412 } 413 414 template <typename T> 415 static void op(sdbusplus::SdBusInterface* intf, sd_bus_message* m, T&& t) 416 { 417 read<T, Args...>(intf, m, t); 418 } 419 }; 420 421 /** @brief Specialization of read_single for std::monostate. */ 422 template <typename S> 423 requires(std::is_same_v<S, std::monostate>) 424 struct read_single<S> 425 { 426 template <typename T> 427 static void op(sdbusplus::SdBusInterface*, sd_bus_message*, T&&) 428 {} 429 }; 430 431 template <typename T> 432 void tuple_item_read(sdbusplus::SdBusInterface* intf, sd_bus_message* m, T&& t) 433 { 434 sdbusplus::message::read(intf, m, t); 435 } 436 437 template <int Index> 438 struct ReadHelper 439 { 440 template <typename... Fields> 441 static void op(sdbusplus::SdBusInterface* intf, sd_bus_message* m, 442 std::tuple<Fields...> field_tuple) 443 { 444 auto& field = std::get<Index - 1>(field_tuple); 445 446 if constexpr (Index > 1) 447 { 448 ReadHelper<Index - 1>::op(intf, m, std::move(field_tuple)); 449 } 450 451 tuple_item_read(intf, m, field); 452 } 453 }; 454 455 /** @brief Read a tuple from the sd_bus_message. 456 * 457 * @tparam Tuple - The tuple type to read. 458 * @param[out] t - The tuple to read into. 459 * 460 * A tuple of 2 or more entries can be read as a set with 461 * sd_bus_message_read. 462 * 463 * A tuple of 1 entry can be read with sd_bus_message_read_basic. 464 * 465 * A tuple of 0 entries is a no-op. 466 * 467 */ 468 template <typename Tuple> 469 void read_tuple(sdbusplus::SdBusInterface* intf, sd_bus_message* m, Tuple&& t) 470 { 471 if constexpr (std::tuple_size_v<Tuple> >= 2) 472 { 473 ReadHelper<std::tuple_size_v<Tuple>>::op(intf, m, std::move(t)); 474 } 475 else if constexpr (std::tuple_size_v<Tuple> == 1) 476 { 477 using itemType = decltype(std::get<0>(t)); 478 read_single_t<itemType>::op(intf, m, 479 std::forward<itemType>(std::get<0>(t))); 480 } 481 } 482 483 /** @brief Group a sequence of C++ types for reading from an sd_bus_message. 484 * @tparam Tuple - A tuple of previously analyzed types. 485 * @tparam Arg - The argument to analyze for grouping. 486 * 487 * Specialization for when can_read_multiple_v<Arg> is true. 488 */ 489 template <typename Tuple, typename Arg> 490 std::enable_if_t<can_read_multiple_v<types::details::type_id_downcast_t<Arg>>> 491 read_grouping(sdbusplus::SdBusInterface* intf, sd_bus_message* m, Tuple&& t, 492 Arg&& arg); 493 /** @brief Group a sequence of C++ types for reading from an sd_bus_message. 494 * @tparam Tuple - A tuple of previously analyzed types. 495 * @tparam Arg - The argument to analyze for grouping. 496 * 497 * Specialization for when can_read_multiple_v<Arg> is false. 498 */ 499 template <typename Tuple, typename Arg> 500 std::enable_if_t<!can_read_multiple_v<types::details::type_id_downcast_t<Arg>>> 501 read_grouping(sdbusplus::SdBusInterface* intf, sd_bus_message* m, Tuple&& t, 502 Arg&& arg); 503 /** @brief Group a sequence of C++ types for reading from an sd_bus_message. 504 * @tparam Tuple - A tuple of previously analyzed types. 505 * @tparam Arg - The argument to analyze for grouping. 506 * @tparam Rest - The remaining arguments left to analyze. 507 * 508 * Specialization for when can_read_multiple_v<Arg> is true. 509 */ 510 template <typename Tuple, typename Arg, typename... Rest> 511 std::enable_if_t<can_read_multiple_v<types::details::type_id_downcast_t<Arg>>> 512 read_grouping(sdbusplus::SdBusInterface* intf, sd_bus_message* m, Tuple&& t, 513 Arg&& arg, Rest&&... rest); 514 /** @brief Group a sequence of C++ types for reading from an sd_bus_message. 515 * @tparam Tuple - A tuple of previously analyzed types. 516 * @tparam Arg - The argument to analyze for grouping. 517 * @tparam Rest - The remaining arguments left to analyze. 518 * 519 * Specialization for when can_read_multiple_v<Arg> is false. 520 */ 521 template <typename Tuple, typename Arg, typename... Rest> 522 std::enable_if_t<!can_read_multiple_v<types::details::type_id_downcast_t<Arg>>> 523 read_grouping(sdbusplus::SdBusInterface* intf, sd_bus_message* m, Tuple&& t, 524 Arg&& arg, Rest&&... rest); 525 526 template <typename Tuple, typename Arg> 527 std::enable_if_t<can_read_multiple_v<types::details::type_id_downcast_t<Arg>>> 528 read_grouping(sdbusplus::SdBusInterface* intf, sd_bus_message* m, Tuple&& t, 529 Arg&& arg) 530 { 531 // Last element of a sequence and can_read_multiple, so add it to 532 // the tuple and call read_tuple. 533 534 read_tuple(intf, m, 535 std::tuple_cat(std::forward<Tuple>(t), 536 std::forward_as_tuple(std::forward<Arg>(arg)))); 537 } 538 539 template <typename Tuple, typename Arg> 540 std::enable_if_t<!can_read_multiple_v<types::details::type_id_downcast_t<Arg>>> 541 read_grouping(sdbusplus::SdBusInterface* intf, sd_bus_message* m, Tuple&& t, 542 Arg&& arg) 543 { 544 // Last element of a sequence but !can_read_multiple, so call 545 // read_tuple on the previous elements and separately this single 546 // element. 547 548 read_tuple(intf, m, std::forward<Tuple>(t)); 549 read_tuple(intf, m, std::forward_as_tuple(std::forward<Arg>(arg))); 550 } 551 552 template <typename Tuple, typename Arg, typename... Rest> 553 std::enable_if_t<can_read_multiple_v<types::details::type_id_downcast_t<Arg>>> 554 read_grouping(sdbusplus::SdBusInterface* intf, sd_bus_message* m, Tuple&& t, 555 Arg&& arg, Rest&&... rest) 556 { 557 // Not the last element of a sequence and can_read_multiple, so add it 558 // to the tuple and keep grouping. 559 560 read_grouping(intf, m, 561 std::tuple_cat(std::forward<Tuple>(t), 562 std::forward_as_tuple(std::forward<Arg>(arg))), 563 std::forward<Rest>(rest)...); 564 } 565 566 template <typename Tuple, typename Arg, typename... Rest> 567 std::enable_if_t<!can_read_multiple_v<types::details::type_id_downcast_t<Arg>>> 568 read_grouping(sdbusplus::SdBusInterface* intf, sd_bus_message* m, Tuple&& t, 569 Arg&& arg, Rest&&... rest) 570 { 571 // Not the last element of a sequence but !can_read_multiple, so call 572 // read_tuple on the previous elements and separately this single 573 // element and then group the remaining elements. 574 575 read_tuple(intf, m, std::forward<Tuple>(t)); 576 read_tuple(intf, m, std::forward_as_tuple(std::forward<Arg>(arg))); 577 read_grouping(intf, m, std::make_tuple(), std::forward<Rest>(rest)...); 578 } 579 580 } // namespace details 581 582 template <typename... Args> 583 void read(sdbusplus::SdBusInterface* intf, sd_bus_message* m, Args&&... args) 584 { 585 details::read_grouping(intf, m, std::make_tuple(), 586 std::forward<Args>(args)...); 587 } 588 589 } // namespace message 590 591 } // namespace sdbusplus 592