1 #pragma once 2 3 #include <systemd/sd-bus.h> 4 #include <systemd/sd-event.h> 5 6 #include <sdbusplus/exception.hpp> 7 #include <sdbusplus/message.hpp> 8 #include <sdbusplus/sdbus.hpp> 9 10 #include <algorithm> 11 #include <climits> 12 #include <exception> 13 #include <memory> 14 #include <optional> 15 #include <string> 16 #include <vector> 17 18 #ifdef __clang__ 19 #pragma clang diagnostic push 20 #pragma clang diagnostic ignored "-Wc99-extensions" 21 #endif 22 23 namespace sdbusplus 24 { 25 26 namespace bus 27 { 28 29 using busp_t = sd_bus*; 30 struct bus; 31 32 /* Methods for getting a new bus connection. 33 * 34 * There are two different bus types: 35 * - system 36 * - session ("user") 37 * 38 * If you call either `new_default` or `new_bus` you end up with a connection 39 * to the default bus based on the current user; system if root, session 40 * otherwise. 41 * 42 * sd-bus uses the word "default" to refer to a shared bus connection that it 43 * saves in thread-local-storage. The `new_default*` functions give a 44 * connection to this thread-local-storage bus connection and not neceesarily a 45 * new, unique bus connection. This can be a very important distinction, 46 * such as when writing test-cases that might require having both a server 47 * and client connection. 48 * 49 * If you only expect to have a single bus connection in your process with a 50 * single thread interacting with that connection, `new_default` is a fine 51 * option. Otherwise, you likely want `new_bus`. 52 */ 53 54 /** @brief Get the shared instance of the 'default' bus. */ 55 bus new_default(); 56 /** @brief Get a new instance of a bus. */ 57 bus new_bus(); 58 /** @brief Get a new instance of the 'user' session bus. */ 59 bus new_user(); 60 /** @brief Get a new instance of the 'system' bus. */ 61 bus new_system(); 62 /** @brief Get the shared instance of the 'user' session bus. */ 63 bus new_default_user(); 64 /** @brief Get the shared instance of the 'system' bus. */ 65 bus new_default_system(); 66 67 namespace details 68 { 69 70 /** @brief unique_ptr functor to release a bus reference. */ 71 struct BusDeleter 72 { 73 BusDeleter() = delete; BusDeletersdbusplus::bus::details::BusDeleter74 explicit BusDeleter(SdBusInterface* interface) : m_interface(interface) {} 75 operator ()sdbusplus::bus::details::BusDeleter76 void operator()(sd_bus* ptr) const 77 { 78 (m_interface->*deleter)(ptr); 79 } 80 81 SdBusInterface* m_interface; 82 decltype(&SdBusInterface::sd_bus_unref) deleter = 83 &SdBusInterface::sd_bus_unref; 84 }; 85 86 /** @brief Convert a vector of strings to c-style char** array. */ 87 class Strv 88 { 89 public: 90 ~Strv() = default; 91 Strv() = delete; 92 Strv(const Strv&) = delete; 93 Strv& operator=(const Strv&) = delete; 94 Strv(Strv&&) = default; 95 Strv& operator=(Strv&&) = default; 96 Strv(const std::vector<std::string> & v)97 explicit Strv(const std::vector<std::string>& v) 98 { 99 std::transform(v.begin(), v.end(), std::back_inserter(ptrs), 100 [](const auto& i) { return i.c_str(); }); 101 ptrs.push_back(nullptr); 102 } 103 operator char**()104 explicit operator char**() 105 { 106 return const_cast<char**>(&ptrs.front()); 107 } 108 109 private: 110 std::vector<const char*> ptrs; 111 }; 112 113 /* @brief Alias 'bus' to a unique_ptr type for auto-release. */ 114 using bus = std::unique_ptr<sd_bus, BusDeleter>; 115 116 struct bus_friend; 117 118 } // namespace details 119 120 /** @class bus 121 * @brief Provides C++ bindings to the sd_bus_* class functions. 122 */ 123 struct bus 124 { 125 /* Define all of the basic class operations: 126 * Not allowed: 127 * - Default constructor to avoid nullptrs. 128 * - Copy operations due to internal unique_ptr. 129 * Allowed: 130 * - Move operations. 131 * - Destructor. 132 */ 133 bus() = delete; 134 bus(const bus&) = delete; 135 bus& operator=(const bus&) = delete; 136 137 bus(bus&&) = default; 138 bus& operator=(bus&&) = default; 139 ~bus() = default; 140 141 bus(busp_t b, sdbusplus::SdBusInterface* intf); 142 143 /** @brief Conversion constructor from 'busp_t'. 144 * 145 * Increments ref-count of the bus-pointer and releases it when done. 146 */ 147 explicit bus(busp_t b); 148 149 /** @brief Constructor for 'bus'. 150 * 151 * Takes ownership of the bus-pointer and releases it when done. 152 */ 153 bus(busp_t b, std::false_type); 154 155 /** @brief Sets the bus to be closed when this handle is destroyed. */ set_should_closesdbusplus::bus::bus156 void set_should_close(bool shouldClose) 157 { 158 if (shouldClose) 159 { 160 _bus.get_deleter().deleter = 161 &SdBusInterface::sd_bus_flush_close_unref; 162 } 163 else 164 { 165 _bus.get_deleter().deleter = &SdBusInterface::sd_bus_unref; 166 } 167 } 168 169 /** @brief Release ownership of the stored bus-pointer. */ releasesdbusplus::bus::bus170 busp_t release() 171 { 172 return _bus.release(); 173 } 174 175 /** @brief Flush all of the outstanding work on the bus. */ flushsdbusplus::bus::bus176 void flush() 177 { 178 int r = _intf->sd_bus_flush(_bus.get()); 179 if (r < 0) 180 { 181 throw exception::SdBusError(-r, "sd_bus_flush"); 182 } 183 } 184 185 /** @brief Close the connection to the dbus daemon. */ closesdbusplus::bus::bus186 void close() 187 { 188 _intf->sd_bus_close(_bus.get()); 189 } 190 191 /** @brief Determine if the bus is open to the broker */ is_opensdbusplus::bus::bus192 bool is_open() 193 { 194 int r = _intf->sd_bus_is_open(_bus.get()); 195 if (r < 0) 196 { 197 throw exception::SdBusError(-r, "sd_bus_is_open"); 198 } 199 return r; 200 } 201 202 /** @brief Wait for new dbus messages or signals. 203 * 204 * @param[in] timeout_us - Timeout in usec. 205 */ waitsdbusplus::bus::bus206 int wait(uint64_t timeout_us) 207 { 208 int r = _intf->sd_bus_wait(_bus.get(), timeout_us); 209 if (r < 0) 210 { 211 throw exception::SdBusError(-r, "sd_bus_wait"); 212 } 213 return r; 214 } 215 waitsdbusplus::bus::bus216 int wait(std::optional<SdBusDuration> timeout = std::nullopt) 217 { 218 return wait(timeout ? timeout->count() : UINT64_MAX); 219 } 220 221 /** @brief Process waiting dbus messages or signals. */ processsdbusplus::bus::bus222 auto process() 223 { 224 sd_bus_message* m = nullptr; 225 int r = _intf->sd_bus_process(_bus.get(), &m); 226 if (current_exception) 227 { 228 auto ex = std::exchange(current_exception, nullptr); 229 std::rethrow_exception(ex); 230 } 231 if (r < 0) 232 { 233 throw exception::SdBusError(-r, "sd_bus_process"); 234 } 235 236 return message_t(m, _intf, std::false_type()); 237 } 238 239 /** @brief Process waiting dbus messages or signals, discarding unhandled. 240 */ process_discardsdbusplus::bus::bus241 auto process_discard() 242 { 243 int r = _intf->sd_bus_process(_bus.get(), nullptr); 244 if (current_exception) 245 { 246 auto ex = std::exchange(current_exception, nullptr); 247 std::rethrow_exception(ex); 248 } 249 if (r < 0) 250 { 251 throw exception::SdBusError(-r, "sd_bus_process discard"); 252 } 253 return r > 0; 254 } 255 256 /** @brief Process waiting dbus messages or signals forever, discarding 257 * unhandled. 258 */ process_loopsdbusplus::bus::bus259 [[noreturn]] void process_loop() 260 { 261 while (true) 262 { 263 process_discard(); 264 wait(); 265 } 266 } 267 268 /** @brief Claim a service name on the dbus. 269 * 270 * @param[in] service - The service name to claim. 271 */ request_namesdbusplus::bus::bus272 void request_name(const char* service) 273 { 274 int r = _intf->sd_bus_request_name( 275 _bus.get(), service, 276 (SD_BUS_NAME_ALLOW_REPLACEMENT | SD_BUS_NAME_REPLACE_EXISTING)); 277 if (r < 0) 278 { 279 throw exception::SdBusError(-r, "sd_bus_request_name"); 280 } 281 } 282 283 /** @brief Create a method_call message. 284 * 285 * @param[in] service - The service to call. 286 * @param[in] objpath - The object's path for the call. 287 * @param[in] interf - The object's interface to call. 288 * @param[in] method - The object's method to call. 289 * 290 * @return A newly constructed message. 291 */ new_method_callsdbusplus::bus::bus292 auto new_method_call(const char* service, const char* objpath, 293 const char* interf, const char* method) 294 { 295 sd_bus_message* m = nullptr; 296 int r = _intf->sd_bus_message_new_method_call(_bus.get(), &m, service, 297 objpath, interf, method); 298 if (r < 0) 299 { 300 throw exception::SdBusError(-r, "sd_bus_message_new_method_call"); 301 } 302 303 return message_t(m, _intf, std::false_type()); 304 } 305 306 /** @brief Create a signal message. 307 * 308 * @param[in] objpath - The object's path for the signal. 309 * @param[in] interf - The object's interface for the signal. 310 * @param[in] member - The signal name. 311 * 312 * @return A newly constructed message. 313 */ new_signalsdbusplus::bus::bus314 auto new_signal(const char* objpath, const char* interf, const char* member) 315 { 316 sd_bus_message* m = nullptr; 317 int r = _intf->sd_bus_message_new_signal(_bus.get(), &m, objpath, 318 interf, member); 319 if (r < 0) 320 { 321 throw exception::SdBusError(-r, "sd_bus_message_new_signal"); 322 } 323 324 return message_t(m, _intf, std::false_type()); 325 } 326 327 /** @brief Perform a message call. 328 * Errors generated by this call come from underlying dbus 329 * related errors *AND* from any method call that results 330 * in a METHOD_ERROR. This means you do not need to check 331 * is_method_error() on the returned message. 332 * 333 * @param[in] m - The method_call message. 334 * @param[in] timeout_us - The timeout for the method call. 335 * 336 * @return The response message. 337 */ callsdbusplus::bus::bus338 auto call(message_t& m, uint64_t timeout_us) 339 { 340 sd_bus_error error = SD_BUS_ERROR_NULL; 341 sd_bus_message* reply = nullptr; 342 int r = 343 _intf->sd_bus_call(_bus.get(), m.get(), timeout_us, &error, &reply); 344 if (r < 0) 345 { 346 throw exception::SdBusError(&error, "sd_bus_call"); 347 } 348 349 return message_t(reply, _intf, std::false_type()); 350 } callsdbusplus::bus::bus351 auto call(message_t& m, std::optional<SdBusDuration> timeout = std::nullopt) 352 { 353 return call(m, timeout ? timeout->count() : 0); 354 } 355 356 /** @brief Perform a message call, ignoring the reply. 357 * 358 * @param[in] m - The method_call message. 359 * @param[in] timeout_us - The timeout for the method call. 360 */ call_noreplysdbusplus::bus::bus361 void call_noreply(message_t& m, uint64_t timeout_us) 362 { 363 sd_bus_error error = SD_BUS_ERROR_NULL; 364 int r = _intf->sd_bus_call(_bus.get(), m.get(), timeout_us, &error, 365 nullptr); 366 if (r < 0) 367 { 368 throw exception::SdBusError(&error, "sd_bus_call noreply"); 369 } 370 } call_noreplysdbusplus::bus::bus371 auto call_noreply(message_t& m, 372 std::optional<SdBusDuration> timeout = std::nullopt) 373 { 374 return call_noreply(m, timeout ? timeout->count() : 0); 375 } 376 377 /** @brief Perform a message call, ignoring the reply and any errors 378 * in the dbus stack. 379 * 380 * @param[in] m - The method_call message. 381 * @param[in] timeout_us - The timeout for the method call. 382 */ call_noreply_noerrorsdbusplus::bus::bus383 void call_noreply_noerror(message_t& m, uint64_t timeout_us) 384 { 385 try 386 { 387 call_noreply(m, timeout_us); 388 } 389 catch (const exception::SdBusError&) 390 { 391 // Intentionally ignoring these sd_bus errors 392 } 393 } call_noreply_noerrorsdbusplus::bus::bus394 auto call_noreply_noerror( 395 message_t& m, std::optional<SdBusDuration> timeout = std::nullopt) 396 { 397 return call_noreply_noerror(m, timeout ? timeout->count() : 0); 398 } 399 400 /** @brief Get the bus unique name. Ex: ":1.11". 401 * 402 * @return The bus unique name. 403 */ get_unique_namesdbusplus::bus::bus404 auto get_unique_name() 405 { 406 const char* unique = nullptr; 407 _intf->sd_bus_get_unique_name(_bus.get(), &unique); 408 return std::string(unique); 409 } 410 get_fdsdbusplus::bus::bus411 auto get_fd() 412 { 413 return _intf->sd_bus_get_fd(_bus.get()); 414 } 415 get_eventssdbusplus::bus::bus416 auto get_events() 417 { 418 return _intf->sd_bus_get_events(_bus.get()); 419 } 420 get_timeoutsdbusplus::bus::bus421 auto get_timeout(uint64_t* timeout) 422 { 423 return _intf->sd_bus_get_timeout(_bus.get(), timeout); 424 } 425 426 /** @brief Attach the bus with a sd-event event loop object. 427 * 428 * @param[in] event - sd_event object. 429 * @param[in] priority - priority of bus event source. 430 */ attach_eventsdbusplus::bus::bus431 void attach_event(sd_event* event, int priority) 432 { 433 _intf->sd_bus_attach_event(_bus.get(), event, priority); 434 } 435 436 /** @brief Detach the bus from its sd-event event loop object */ detach_eventsdbusplus::bus::bus437 void detach_event() 438 { 439 _intf->sd_bus_detach_event(_bus.get()); 440 } 441 442 /** @brief Get the sd-event event loop object of the bus */ get_eventsdbusplus::bus::bus443 auto get_event() 444 { 445 return _intf->sd_bus_get_event(_bus.get()); 446 } 447 448 /** @brief Wrapper for sd_bus_emit_interfaces_added_strv 449 * 450 * In general the similarly named server::object::object API should 451 * be used to manage emission of ObjectManager signals in favor 452 * of this one. Provided here for complex usage scenarios. 453 * 454 * @param[in] path - The path to forward. 455 * @param[in] ifaces - The interfaces to forward. 456 */ 457 void emit_interfaces_added(const char* path, 458 const std::vector<std::string>& ifaces); 459 460 /** @brief Wrapper for sd_bus_emit_interfaces_removed_strv 461 * 462 * In general the similarly named server::object::object API should 463 * be used to manage emission of ObjectManager signals in favor 464 * of this one. Provided here for complex usage scenarios. 465 * 466 * @param[in] path - The path to forward. 467 * @param[in] ifaces - The interfaces to forward. 468 */ 469 void emit_interfaces_removed(const char* path, 470 const std::vector<std::string>& ifaces); 471 472 /** @brief Wrapper for sd_bus_emit_object_added 473 * 474 * In general the similarly named server::object::object API should 475 * be used to manage emission of ObjectManager signals in favor 476 * of this one. Provided here for complex usage scenarios. 477 * 478 * @param[in] path - The path to forward to sd_bus_emit_object_added 479 */ emit_object_addedsdbusplus::bus::bus480 void emit_object_added(const char* path) 481 { 482 _intf->sd_bus_emit_object_added(_bus.get(), path); 483 } 484 485 /** @brief Wrapper for sd_bus_emit_object_removed 486 * 487 * In general the similarly named server::object::object API should 488 * be used to manage emission of ObjectManager signals in favor 489 * of this one. Provided here for complex usage scenarios. 490 * 491 * @param[in] path - The path to forward to sd_bus_emit_object_removed 492 */ emit_object_removedsdbusplus::bus::bus493 void emit_object_removed(const char* path) 494 { 495 _intf->sd_bus_emit_object_removed(_bus.get(), path); 496 } 497 498 /** @brief Wrapper for sd_bus_list_names. 499 * 500 * @return A vector of strings containing the 'acquired' names from 501 * sd_bus_list_names. 502 */ list_names_acquiredsdbusplus::bus::bus503 auto list_names_acquired() 504 { 505 char** names = nullptr; 506 507 _intf->sd_bus_list_names(_bus.get(), &names, nullptr); 508 509 std::vector<std::string> result; 510 for (auto ptr = names; ptr && *ptr; ++ptr) 511 { 512 result.push_back(*ptr); 513 free(*ptr); 514 } 515 free(names); 516 517 return result; 518 } 519 520 /** @brief Get the SdBusInterface used by this bus. 521 * 522 * @return A pointer to the SdBusInterface used by this bus. 523 */ getInterfacesdbusplus::bus::bus524 sdbusplus::SdBusInterface* getInterface() 525 { 526 return _intf; 527 } 528 set_current_exceptionsdbusplus::bus::bus529 void set_current_exception(std::exception_ptr exception) 530 { 531 current_exception = exception; 532 } 533 534 friend struct details::bus_friend; 535 536 protected: getsdbusplus::bus::bus537 busp_t get() noexcept 538 { 539 return _bus.get(); 540 } 541 sdbusplus::SdBusInterface* _intf; 542 details::bus _bus; 543 544 private: 545 std::exception_ptr current_exception; 546 }; 547 548 namespace details 549 { 550 551 // Some sdbusplus classes need to be able to pass the underlying bus pointer 552 // along to sd_bus calls, but we don't want to make it available for everyone. 553 // Define a class which can be inherited explicitly (intended for internal users 554 // only) to get the underlying bus pointer. 555 struct bus_friend 556 { get_buspsdbusplus::bus::details::bus_friend557 static busp_t get_busp(sdbusplus::bus::bus& b) noexcept 558 { 559 return b.get(); 560 } 561 }; 562 563 } // namespace details 564 565 } // namespace bus 566 567 using bus_t = bus::bus; 568 569 /** @brief Get the dbus bus from the message. 570 * 571 * @return The dbus bus. 572 */ get_bus() const573 inline bus_t message_t::get_bus() const 574 { 575 sd_bus* b = nullptr; 576 b = _intf->sd_bus_message_get_bus(_msg.get()); 577 return bus_t(b, _intf); 578 } 579 580 } // namespace sdbusplus 581 582 #ifdef __clang__ 583 #pragma clang diagnostic pop 584 #endif 585