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