xref: /openbmc/sdbusplus/include/sdbusplus/bus.hpp (revision fc0bb996)
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