/** * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2024 Stanislav Angelovic * * @file IConnection.h * * Created on: Nov 8, 2016 * Project: sdbus-c++ * Description: High-level D-Bus IPC C++ library based on sd-bus * * This file is part of sdbus-c++. * * sdbus-c++ is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * sdbus-c++ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with sdbus-c++. If not, see . */ #ifndef SDBUS_CXX_ICONNECTION_H_ #define SDBUS_CXX_ICONNECTION_H_ #include #include #include #include #include #include // Forward declarations struct sd_bus; struct sd_event; namespace sdbus { class Message; class ObjectPath; class BusName; using ServiceName = BusName; } namespace sdbus { /********************************************//** * @class IConnection * * An interface to D-Bus bus connection. Incorporates implementation * of both synchronous and asynchronous D-Bus I/O event loop. * * All methods throw sdbus::Error in case of failure. All methods in * this class are thread-aware, but not thread-safe. * ***********************************************/ class IConnection { public: struct PollData; virtual ~IConnection() = default; /*! * @brief Enters I/O event loop on this bus connection * * The incoming D-Bus messages are processed in the loop. The method * blocks indefinitely, until unblocked through leaveEventLoop(). * * @throws sdbus::Error in case of failure */ virtual void enterEventLoop() = 0; /*! * @brief Enters I/O event loop on this bus connection in a separate thread * * The same as enterEventLoop, except that it doesn't block * because it runs the loop in a separate, internally managed thread. */ virtual void enterEventLoopAsync() = 0; /*! * @brief Leaves the I/O event loop running on this bus connection * * This causes the loop to exit and frees the thread serving the loop * * @throws sdbus::Error in case of failure */ virtual void leaveEventLoop() = 0; /*! * @brief Attaches the bus connection to an sd-event event loop * * @param[in] event sd-event event loop object * @param[in] priority Specified priority * * @throws sdbus::Error in case of failure * * See `man sd_bus_attach_event'. */ virtual void attachSdEventLoop(sd_event *event, int priority = 0) = 0; /*! * @brief Detaches the bus connection from an sd-event event loop * * @throws sdbus::Error in case of failure */ virtual void detachSdEventLoop() = 0; /*! * @brief Gets current sd-event event loop for the bus connection * * @return Pointer to event loop object if attached, nullptr otherwise */ virtual sd_event *getSdEventLoop() = 0; /*! * @brief Returns fd's, I/O events and timeout data to be used in an external event loop * * This function is useful to hook up a bus connection object with an * external (like GMainLoop, boost::asio, etc.) or manual event loop * involving poll() or a similar I/O polling call. * * Before **each** invocation of the I/O polling call, this function * should be invoked. Returned PollData::fd file descriptor should * be polled for the events indicated by PollData::events, and the I/O * call should block for that up to the returned PollData::timeout. * * Additionally, returned PollData::eventFd should be polled for POLLIN * events. * * After each I/O polling call the bus connection needs to process * incoming or outgoing data, by invoking processPendingEvent(). * * Note that the returned timeout should be considered only a maximum * sleeping time. It is permissible (and even expected) that shorter * timeouts are used by the calling program, in case other event sources * are polled in the same event loop. Note that the returned time-value * is absolute, based of CLOCK_MONOTONIC and specified in microseconds. * Use PollData::getPollTimeout() to have the timeout value converted * in a form that can be passed to poll(2). * * The bus connection conveniently integrates sd-event event loop. * To attach the bus connection to an sd-event event loop, use * attachSdEventLoop() function. * * @throws sdbus::Error in case of failure */ [[nodiscard]] virtual PollData getEventLoopPollData() const = 0; /*! * @brief Processes a pending event * * @returns True if an event was processed, false if no operations were pending * * This function drives the D-Bus connection. It processes pending I/O events. * Queued outgoing messages (or parts thereof) are sent out. Queued incoming * messages are dispatched to registered callbacks. Timeouts are recalculated. * * It returns false when no operations were pending and true if a message was * processed. When false is returned the caller should synchronously poll for * I/O events before calling into processPendingEvent() again. * Don't forget to call getEventLoopPollData() each time before the next poll. * * You don't need to directly call this method or getEventLoopPollData() method * when using convenient, internal bus connection event loops through * enterEventLoop() or enterEventLoopAsync() calls, or when the bus is * connected to an sd-event event loop through attachSdEventLoop(). * It is invoked automatically when necessary. * * @throws sdbus::Error in case of failure */ virtual bool processPendingEvent() = 0; /*! * @brief Provides access to the currently processed D-Bus message * * This method provides access to the currently processed incoming D-Bus message. * "Currently processed" means that the registered callback handler(s) for that message * are being invoked. This method is meant to be called from within a callback handler * (e.g. from a D-Bus signal handler, or async method reply handler, etc.). In such a case it is * guaranteed to return a valid D-Bus message instance for which the handler is called. * If called from other contexts/threads, it may return a valid or invalid message, depending * on whether a message was processed or not at the time of the call. * * @return Currently processed D-Bus message */ [[nodiscard]] virtual Message getCurrentlyProcessedMessage() const = 0; /*! * @brief Sets general method call timeout * * @param[in] timeout Timeout value in microseconds * * General method call timeout is used for all method calls upon this connection. * Method call-specific timeout overrides this general setting. * * Supported by libsystemd>=v240. * * @throws sdbus::Error in case of failure */ virtual void setMethodCallTimeout(uint64_t timeout) = 0; /*! * @copydoc IConnection::setMethodCallTimeout(uint64_t) */ template void setMethodCallTimeout(const std::chrono::duration<_Rep, _Period>& timeout); /*! * @brief Gets general method call timeout * * @return Timeout value in microseconds * * Supported by libsystemd>=v240. * * @throws sdbus::Error in case of failure */ [[nodiscard]] virtual uint64_t getMethodCallTimeout() const = 0; /*! * @brief Adds an ObjectManager at the specified D-Bus object path * @param[in] objectPath Object path at which the ObjectManager interface shall be installed * * Creates an ObjectManager interface at the specified object path on * the connection. This is a convenient way to interrogate a connection * to see what objects it has. * * This call creates a floating registration. The ObjectManager will * be there for the object path until the connection is destroyed. * * Another, recommended way to add object managers is directly through IObject API. * * @throws sdbus::Error in case of failure */ virtual void addObjectManager(const ObjectPath& objectPath) = 0; /*! * @brief Adds an ObjectManager at the specified D-Bus object path * @param[in] objectPath Object path at which the ObjectManager interface shall be installed * @return Slot handle owning the registration * * Creates an ObjectManager interface at the specified object path on * the connection. This is a convenient way to interrogate a connection * to see what objects it has. * * This call returns an owning slot. The lifetime of the ObjectManager * interface is bound to the lifetime of the returned slot instance. * * Another, recommended way to add object managers is directly through IObject API. * * @throws sdbus::Error in case of failure */ [[nodiscard]] virtual Slot addObjectManager(const ObjectPath& objectPath, return_slot_t) = 0; /*! * @brief Installs a floating match rule for messages received on this bus connection * * @param[in] match Match expression to filter incoming D-Bus message * @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule * * The method installs a match rule for messages received on the specified bus connection. * The syntax of the match rule expression passed in match is described in the D-Bus specification. * The specified handler function callback is called for each incoming message matching the specified * expression. The match is installed synchronously when connected to a bus broker, i.e. the call * sends a control message requesting the match to be added to the broker and waits until the broker * confirms the match has been installed successfully. * * The method installs a floating match rule for messages received on the specified bus connection. * Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule * is bound to the lifetime of the bus connection. * * For more information, consult `man sd_bus_add_match`. * * @throws sdbus::Error in case of failure */ virtual void addMatch(const std::string& match, message_handler callback) = 0; /*! * @brief Installs a match rule for messages received on this bus connection * * @param[in] match Match expression to filter incoming D-Bus message * @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule * @return RAII-style slot handle representing the ownership of the subscription * * The method installs a match rule for messages received on the specified bus connection. * The syntax of the match rule expression passed in match is described in the D-Bus specification. * The specified handler function callback is called for each incoming message matching the specified * expression. The match is installed synchronously when connected to a bus broker, i.e. the call * sends a control message requesting the match to be added to the broker and waits until the broker * confirms the match has been installed successfully. * * The lifetime of the match rule is bound to the lifetime of the returned slot instance. Destroying * the slot instance implies uninstalling of the match rule from the bus connection. * * For more information, consult `man sd_bus_add_match`. * * @throws sdbus::Error in case of failure */ [[nodiscard]] virtual Slot addMatch(const std::string& match, message_handler callback, return_slot_t) = 0; /*! * @brief Asynchronously installs a floating match rule for messages received on this bus connection * * @param[in] match Match expression to filter incoming D-Bus message * @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule * @param[in] installCallback Callback handler to be called upon processing an inbound D-Bus message matching the rule * * This method operates the same as `addMatch()` above, just that it installs the match rule asynchronously, * in a non-blocking fashion. A request is sent to the broker, but the call does not wait for a response. * The `installCallback' callable is called when the response is later received, with the response message * from the broker as parameter. If it's an empty function object, a default implementation is used that * terminates the bus connection should installing the match fail. * * The method installs a floating match rule for messages received on the specified bus connection. * Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule * is bound to the lifetime of the bus connection. * * For more information, consult `man sd_bus_add_match_async`. * * @throws sdbus::Error in case of failure */ virtual void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) = 0; /*! * @brief Asynchronously installs a match rule for messages received on this bus connection * * @param[in] match Match expression to filter incoming D-Bus message * @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule * @param[in] installCallback Callback handler to be called upon processing an inbound D-Bus message matching the rule * @return RAII-style slot handle representing the ownership of the subscription * * This method operates the same as `addMatch()` above, just that it installs the match rule asynchronously, * in a non-blocking fashion. A request is sent to the broker, but the call does not wait for a response. * The `installCallback' callable is called when the response is later received, with the response message * from the broker as parameter. If it's an empty function object, a default implementation is used that * terminates the bus connection should installing the match fail. * * The lifetime of the match rule is bound to the lifetime of the returned slot instance. Destroying * the slot instance implies the uninstalling of the match rule from the bus connection. * * For more information, consult `man sd_bus_add_match_async`. * * @throws sdbus::Error in case of failure */ [[nodiscard]] virtual Slot addMatchAsync( const std::string& match , message_handler callback , message_handler installCallback , return_slot_t ) = 0; /*! * @brief Retrieves the unique name of a connection. E.g. ":1.xx" * * @throws sdbus::Error in case of failure */ [[nodiscard]] virtual BusName getUniqueName() const = 0; /*! * @brief Requests a well-known D-Bus service name on a bus * * @param[in] name Name to request * * @throws sdbus::Error in case of failure */ virtual void requestName(const ServiceName& name) = 0; /*! * @brief Releases an acquired well-known D-Bus service name on a bus * * @param[in] name Name to release * * @throws sdbus::Error in case of failure */ virtual void releaseName(const ServiceName& name) = 0; /*! * @struct PollData * * Carries poll data needed for integration with external event loop implementations. * * See getEventLoopPollData() for more info. */ struct PollData { /*! * The read fd to be monitored by the event loop. */ int fd; /*! * The events to use for poll(2) alongside fd. */ short int events; /*! * Absolute timeout value in microseconds, based of CLOCK_MONOTONIC. * * Call getPollTimeout() to get timeout recalculated to relative timeout that can be passed to poll(2). */ std::chrono::microseconds timeout; /*! * An additional event fd to be monitored by the event loop for POLLIN events. */ int eventFd; /*! * Returns the timeout as relative value from now. * * Returned value is std::chrono::microseconds::max() if the timeout is indefinite. * * @return Relative timeout as a time duration */ [[nodiscard]] std::chrono::microseconds getRelativeTimeout() const; /*! * Returns relative timeout in the form which can be passed as argument 'timeout' to poll(2) * * @return -1 if the timeout is indefinite. 0 if the poll(2) shouldn't block. * An integer in milliseconds otherwise. */ [[nodiscard]] int getPollTimeout() const; }; }; template inline void IConnection::setMethodCallTimeout(const std::chrono::duration<_Rep, _Period>& timeout) { auto microsecs = std::chrono::duration_cast(timeout); return setMethodCallTimeout(microsecs.count()); } /*! * @brief Creates/opens D-Bus session bus connection when in a user context, and a system bus connection, otherwise. * * @return Connection instance * * @throws sdbus::Error in case of failure */ [[nodiscard]] std::unique_ptr createBusConnection(); /*! * @brief Creates/opens D-Bus session bus connection with a name when in a user context, and a system bus connection with a name, otherwise. * * @param[in] name Name to request on the connection after its opening * @return Connection instance * * @throws sdbus::Error in case of failure */ [[nodiscard]] std::unique_ptr createBusConnection(const ServiceName& name); /*! * @brief Creates/opens D-Bus system bus connection * * @return Connection instance * * @throws sdbus::Error in case of failure */ [[nodiscard]] std::unique_ptr createSystemBusConnection(); /*! * @brief Creates/opens D-Bus system bus connection with a name * * @param[in] name Name to request on the connection after its opening * @return Connection instance * * @throws sdbus::Error in case of failure */ [[nodiscard]] std::unique_ptr createSystemBusConnection(const ServiceName& name); /*! * @brief Creates/opens D-Bus session bus connection * * @return Connection instance * * @throws sdbus::Error in case of failure */ [[nodiscard]] std::unique_ptr createSessionBusConnection(); /*! * @brief Creates/opens D-Bus session bus connection with a name * * @param[in] name Name to request on the connection after its opening * @return Connection instance * * @throws sdbus::Error in case of failure */ [[nodiscard]] std::unique_ptr createSessionBusConnection(const ServiceName& name); /*! * @brief Creates/opens D-Bus session bus connection at a custom address * * @param[in] address ";"-separated list of addresses of bus brokers to try to connect * @return Connection instance * * @throws sdbus::Error in case of failure * * Consult manual pages for `sd_bus_set_address` of the underlying sd-bus library for more information. */ [[nodiscard]] std::unique_ptr createSessionBusConnectionWithAddress(const std::string& address); /*! * @brief Creates/opens D-Bus system connection on a remote host using ssh * * @param[in] host Name of the host to connect * @return Connection instance * * @throws sdbus::Error in case of failure */ [[nodiscard]] std::unique_ptr createRemoteSystemBusConnection(const std::string& host); /*! * @brief Opens direct D-Bus connection at a custom address * * @param[in] address ";"-separated list of addresses of bus brokers to try to connect to * @return Connection instance * * @throws sdbus::Error in case of failure */ [[nodiscard]] std::unique_ptr createDirectBusConnection(const std::string& address); /*! * @brief Opens direct D-Bus connection at the given file descriptor * * @param[in] fd File descriptor used to communicate directly from/to a D-Bus server * @return Connection instance * * The underlying sdbus-c++ connection instance takes over ownership of fd, so the caller can let it go. * If, however, the call throws an exception, the ownership of fd remains with the caller. * * @throws sdbus::Error in case of failure */ [[nodiscard]] std::unique_ptr createDirectBusConnection(int fd); /*! * @brief Opens direct D-Bus connection at fd as a server * * @param[in] fd File descriptor to use for server DBus connection * @return Server connection instance * * This creates a new, custom bus object in server mode. One can then call createDirectBusConnection() * on client side to connect to this bus. * * The underlying sdbus-c++ connection instance takes over ownership of fd, so the caller can let it go. * If, however, the call throws an exception, the ownership of fd remains with the caller. * * @throws sdbus::Error in case of failure */ [[nodiscard]] std::unique_ptr createServerBus(int fd); /*! * @brief Creates sdbus-c++ bus connection representation out of underlying sd_bus instance * * @param[in] bus File descriptor to use for server DBus connection * @return Connection instance * * This functions is helpful in cases where clients need a custom, tweaked configuration of their * bus object. Since sdbus-c++ does not provide C++ API for all bus connection configuration * functions of the underlying sd-bus library, clients can use these sd-bus functions themselves * to create and configure their sd_bus object, and create sdbus-c++ IConnection on top of it. * * The IConnection instance assumes unique ownership of the provided bus object. The bus object * must have been started by the client before this call. * The bus object will get flushed, closed, and unreffed when the IConnection instance is destroyed. * * @throws sdbus::Error in case of failure * * Code example: * @code * sd_bus* bus{}; * ::sd_bus_new(&bus); * ::sd_bus_set_address(bus, address); * ::sd_bus_set_anonymous(bus, true); * ::sd_bus_start(bus); * auto con = sdbus::createBusConnection(bus); // IConnection consumes sd_bus object * @endcode */ [[nodiscard]] std::unique_ptr createBusConnection(sd_bus *bus); } #endif /* SDBUS_CXX_ICONNECTION_H_ */