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
416 /** @brief Attach the bus with a sd-event event loop object.
417 *
418 * @param[in] event - sd_event object.
419 * @param[in] priority - priority of bus event source.
420 */
attach_eventsdbusplus::bus::bus421 void attach_event(sd_event* event, int priority)
422 {
423 _intf->sd_bus_attach_event(_bus.get(), event, priority);
424 }
425
426 /** @brief Detach the bus from its sd-event event loop object */
detach_eventsdbusplus::bus::bus427 void detach_event()
428 {
429 _intf->sd_bus_detach_event(_bus.get());
430 }
431
432 /** @brief Get the sd-event event loop object of the bus */
get_eventsdbusplus::bus::bus433 auto get_event()
434 {
435 return _intf->sd_bus_get_event(_bus.get());
436 }
437
438 /** @brief Wrapper for sd_bus_emit_interfaces_added_strv
439 *
440 * In general the similarly named server::object::object API should
441 * be used to manage emission of ObjectManager signals in favor
442 * of this one. Provided here for complex usage scenarios.
443 *
444 * @param[in] path - The path to forward.
445 * @param[in] ifaces - The interfaces to forward.
446 */
447 void emit_interfaces_added(const char* path,
448 const std::vector<std::string>& ifaces);
449
450 /** @brief Wrapper for sd_bus_emit_interfaces_removed_strv
451 *
452 * In general the similarly named server::object::object API should
453 * be used to manage emission of ObjectManager signals in favor
454 * of this one. Provided here for complex usage scenarios.
455 *
456 * @param[in] path - The path to forward.
457 * @param[in] ifaces - The interfaces to forward.
458 */
459 void emit_interfaces_removed(const char* path,
460 const std::vector<std::string>& ifaces);
461
462 /** @brief Wrapper for sd_bus_emit_object_added
463 *
464 * In general the similarly named server::object::object API should
465 * be used to manage emission of ObjectManager signals in favor
466 * of this one. Provided here for complex usage scenarios.
467 *
468 * @param[in] path - The path to forward to sd_bus_emit_object_added
469 */
emit_object_addedsdbusplus::bus::bus470 void emit_object_added(const char* path)
471 {
472 _intf->sd_bus_emit_object_added(_bus.get(), path);
473 }
474
475 /** @brief Wrapper for sd_bus_emit_object_removed
476 *
477 * In general the similarly named server::object::object API should
478 * be used to manage emission of ObjectManager signals in favor
479 * of this one. Provided here for complex usage scenarios.
480 *
481 * @param[in] path - The path to forward to sd_bus_emit_object_removed
482 */
emit_object_removedsdbusplus::bus::bus483 void emit_object_removed(const char* path)
484 {
485 _intf->sd_bus_emit_object_removed(_bus.get(), path);
486 }
487
488 /** @brief Wrapper for sd_bus_list_names.
489 *
490 * @return A vector of strings containing the 'acquired' names from
491 * sd_bus_list_names.
492 */
list_names_acquiredsdbusplus::bus::bus493 auto list_names_acquired()
494 {
495 char** names = nullptr;
496
497 _intf->sd_bus_list_names(_bus.get(), &names, nullptr);
498
499 std::vector<std::string> result;
500 for (auto ptr = names; ptr && *ptr; ++ptr)
501 {
502 result.push_back(*ptr);
503 free(*ptr);
504 }
505 free(names);
506
507 return result;
508 }
509
510 /** @brief Get the SdBusInterface used by this bus.
511 *
512 * @return A pointer to the SdBusInterface used by this bus.
513 */
getInterfacesdbusplus::bus::bus514 sdbusplus::SdBusInterface* getInterface()
515 {
516 return _intf;
517 }
518
set_current_exceptionsdbusplus::bus::bus519 void set_current_exception(std::exception_ptr exception)
520 {
521 current_exception = exception;
522 }
523
524 friend struct details::bus_friend;
525
526 protected:
getsdbusplus::bus::bus527 busp_t get() noexcept
528 {
529 return _bus.get();
530 }
531 sdbusplus::SdBusInterface* _intf;
532 details::bus _bus;
533
534 private:
535 std::exception_ptr current_exception;
536 };
537
538 namespace details
539 {
540
541 // Some sdbusplus classes need to be able to pass the underlying bus pointer
542 // along to sd_bus calls, but we don't want to make it available for everyone.
543 // Define a class which can be inherited explicitly (intended for internal users
544 // only) to get the underlying bus pointer.
545 struct bus_friend
546 {
get_buspsdbusplus::bus::details::bus_friend547 static busp_t get_busp(sdbusplus::bus::bus& b) noexcept
548 {
549 return b.get();
550 }
551 };
552
553 } // namespace details
554
555 } // namespace bus
556
557 using bus_t = bus::bus;
558
559 /** @brief Get the dbus bus from the message.
560 *
561 * @return The dbus bus.
562 */
get_bus() const563 inline bus_t message_t::get_bus() const
564 {
565 sd_bus* b = nullptr;
566 b = _intf->sd_bus_message_get_bus(_msg.get());
567 return bus_t(b, _intf);
568 }
569
570 } // namespace sdbusplus
571
572 #ifdef __clang__
573 #pragma clang diagnostic pop
574 #endif
575