forked from Kistler-Group/sdbus-cpp
feat: add support for direct connections (#350)
* feat: add support for direct connections * refactor: simplify a bit, change comments, extend tests * fix: compiler warning about unused variable * docs: add section on direct connections to the tutorial --------- Co-authored-by: Maksim Fedyarov <m.fedyarov@omp.ru> Co-authored-by: Stanislav Angelovič <stanislav.angelovic@protonmail.com>
This commit is contained in:
@@ -21,7 +21,8 @@ Using sdbus-c++ library
|
|||||||
16. [Standard D-Bus interfaces](#standard-d-bus-interfaces)
|
16. [Standard D-Bus interfaces](#standard-d-bus-interfaces)
|
||||||
17. [Representing D-Bus Types in sdbus-c++](#representing-d-bus-types-in-sdbus-c)
|
17. [Representing D-Bus Types in sdbus-c++](#representing-d-bus-types-in-sdbus-c)
|
||||||
18. [Support for match rules](#support-for-match-rules)
|
18. [Support for match rules](#support-for-match-rules)
|
||||||
19. [Conclusion](#conclusion)
|
19. [Using direct (peer-to-peer) D-Bus connections](#using-direct-peer-to-peer-d-bus-connections)
|
||||||
|
20. [Conclusion](#conclusion)
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
------------
|
------------
|
||||||
@@ -1612,6 +1613,62 @@ Support for match rules
|
|||||||
|
|
||||||
`IConnection` class provides `addMatch` method that you can use to install match rules. An associated callback handler will be called upon an incoming message matching given match rule. There is support for both client-owned and floating (library-owned) match rules. Consult `IConnection` header or sdbus-c++ doxygen documentation for more information.
|
`IConnection` class provides `addMatch` method that you can use to install match rules. An associated callback handler will be called upon an incoming message matching given match rule. There is support for both client-owned and floating (library-owned) match rules. Consult `IConnection` header or sdbus-c++ doxygen documentation for more information.
|
||||||
|
|
||||||
|
Using direct (peer-to-peer) D-Bus connections
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
sdbus-c++ provides an API to establish a direct connection between two peers -- between a client and a server, without going via the D-Bus daemon. The methods of interest, which will create a D-Bus server bus, and a client connection to it, respectively, are:
|
||||||
|
|
||||||
|
* `sdbus::createServerBus()` creates and returns a new, custom bus object in server mode, out of provided file descriptor parameter.
|
||||||
|
* `sdbus::createDirectBusConnection()` opens and returns direct D-Bus connection at the provided custom address(es), or at the provided file descriptor.
|
||||||
|
|
||||||
|
Here is an example, extracted from the analogous test case in sdbus-c++ integration tests suite:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#include "Concatenator.h"
|
||||||
|
#include "ConcatenatorProxy.h"
|
||||||
|
#include <sdbus-c++/sdbus-c++.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int fds[2];
|
||||||
|
|
||||||
|
socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
|
||||||
|
|
||||||
|
std::unique_ptr<sdbus::IConnection> serverConnection;
|
||||||
|
std::unique_ptr<sdbus::IConnection> clientConnection;
|
||||||
|
|
||||||
|
std::thread t([&]()
|
||||||
|
{
|
||||||
|
serverConnection = sdbus::createServerBus(fds[0]);
|
||||||
|
// This is necessary so that createDirectBusConnection() below does not block
|
||||||
|
serverConnection->enterEventLoopAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
clientConnection = sdbus::createDirectBusConnection(fds[1]);
|
||||||
|
clientConnection->enterEventLoopAsync();
|
||||||
|
|
||||||
|
t.join();
|
||||||
|
|
||||||
|
// We can now use connection objects in a familiar way, e.g. create adaptor and proxy objects on them, and exchange messages.
|
||||||
|
// Here, using Concatenator IDL-generated bindings example from chapters above:
|
||||||
|
const char* objectPath = "/org/sdbuscpp/concatenator";
|
||||||
|
Concatenator concatenator(*serverConnection, objectPath);
|
||||||
|
const char* emptyDestinationName = ""; // Destination may be empty in case of direct connections
|
||||||
|
ConcatenatorProxy concatenatorProxy(*clientConnection, emptyDestinationName, objectPath);
|
||||||
|
|
||||||
|
// Perform call of concatenate D-Bus method
|
||||||
|
std::vector<int> numbers = {1, 2, 3};
|
||||||
|
std::string separator = ":";
|
||||||
|
auto concatenatedString = concatenatorProxy.concatenate(numbers, separator);
|
||||||
|
assert(concatenatedString == "1:2:3");
|
||||||
|
|
||||||
|
clientConnection->leaveEventLoop();
|
||||||
|
serverConnection->leaveEventLoop();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Conclusion
|
Conclusion
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
@@ -458,6 +458,45 @@ namespace sdbus {
|
|||||||
* @throws sdbus::Error in case of failure
|
* @throws sdbus::Error in case of failure
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createRemoteSystemBusConnection(const std::string& host);
|
[[nodiscard]] std::unique_ptr<sdbus::IConnection> 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<sdbus::IConnection> 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<sdbus::IConnection> 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<sdbus::IConnection> createServerBus(int fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* SDBUS_CXX_ICONNECTION_H_ */
|
#endif /* SDBUS_CXX_ICONNECTION_H_ */
|
||||||
|
@@ -562,6 +562,9 @@ namespace sdbus {
|
|||||||
* should make sure that an I/O event loop is running on that connection, so the proxy
|
* should make sure that an I/O event loop is running on that connection, so the proxy
|
||||||
* may receive incoming signals and asynchronous method replies.
|
* may receive incoming signals and asynchronous method replies.
|
||||||
*
|
*
|
||||||
|
* The destination parameter may be an empty string (useful e.g. in case of direct
|
||||||
|
* D-Bus connections to a custom server bus).
|
||||||
|
*
|
||||||
* Code example:
|
* Code example:
|
||||||
* @code
|
* @code
|
||||||
* auto proxy = sdbus::createProxy(connection, "com.kistler.foo", "/com/kistler/foo");
|
* auto proxy = sdbus::createProxy(connection, "com.kistler.foo", "/com/kistler/foo");
|
||||||
@@ -585,6 +588,9 @@ namespace sdbus {
|
|||||||
* upon that connection in a separate internal thread. Handlers for incoming signals and
|
* upon that connection in a separate internal thread. Handlers for incoming signals and
|
||||||
* asynchronous method replies will be executed in the context of that thread.
|
* asynchronous method replies will be executed in the context of that thread.
|
||||||
*
|
*
|
||||||
|
* The destination parameter may be an empty string (useful e.g. in case of direct
|
||||||
|
* D-Bus connections to a custom server bus).
|
||||||
|
*
|
||||||
* Code example:
|
* Code example:
|
||||||
* @code
|
* @code
|
||||||
* auto proxy = sdbus::createProxy(std::move(connection), "com.kistler.foo", "/com/kistler/foo");
|
* auto proxy = sdbus::createProxy(std::move(connection), "com.kistler.foo", "/com/kistler/foo");
|
||||||
@@ -603,10 +609,14 @@ namespace sdbus {
|
|||||||
* @return Pointer to the object proxy instance
|
* @return Pointer to the object proxy instance
|
||||||
*
|
*
|
||||||
* The provided connection will be used by the proxy to issue calls against the object.
|
* The provided connection will be used by the proxy to issue calls against the object.
|
||||||
* The Object proxy becomes an exclusive owner of this connection, but will not start an event loop
|
* The Object proxy becomes an exclusive owner of this connection, but will not start
|
||||||
* thread on this connection. This is cheap construction and is suitable for short-lived proxies
|
* an event loop thread on this connection. This is cheap construction and is suitable
|
||||||
* created just to execute simple synchronous D-Bus calls and then destroyed. Such blocking request-reply
|
* for short-lived proxies created just to execute simple synchronous D-Bus calls and
|
||||||
* calls will work without an event loop (but signals, async calls, etc. won't).
|
* then destroyed. Such blocking request-reply calls will work without an event loop
|
||||||
|
* (but signals, async calls, etc. won't).
|
||||||
|
*
|
||||||
|
* The destination parameter may be an empty string (useful e.g. in case of direct
|
||||||
|
* D-Bus connections to a custom server bus).
|
||||||
*
|
*
|
||||||
* Code example:
|
* Code example:
|
||||||
* @code
|
* @code
|
||||||
|
@@ -70,6 +70,21 @@ Connection::Connection(std::unique_ptr<ISdBus>&& interface, remote_system_bus_t,
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connection::Connection(std::unique_ptr<ISdBus>&& interface, private_bus_t, const std::string& address)
|
||||||
|
: Connection(std::move(interface), [&](sd_bus** bus) { return iface_->sd_bus_open_direct(bus, address.c_str()); })
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Connection::Connection(std::unique_ptr<ISdBus>&& interface, private_bus_t, int fd)
|
||||||
|
: Connection(std::move(interface), [&](sd_bus** bus) { return iface_->sd_bus_open_direct(bus, fd); })
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Connection::Connection(std::unique_ptr<ISdBus>&& interface, server_bus_t, int fd)
|
||||||
|
: Connection(std::move(interface), [&](sd_bus** bus) { return iface_->sd_bus_open_server(bus, fd); })
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Connection::Connection(std::unique_ptr<ISdBus>&& interface, pseudo_bus_t)
|
Connection::Connection(std::unique_ptr<ISdBus>&& interface, pseudo_bus_t)
|
||||||
: iface_(std::move(interface))
|
: iface_(std::move(interface))
|
||||||
, bus_(openPseudoBus())
|
, bus_(openPseudoBus())
|
||||||
@@ -262,7 +277,7 @@ MethodCall Connection::createMethodCall( const std::string& destination
|
|||||||
|
|
||||||
auto r = iface_->sd_bus_message_new_method_call( bus_.get()
|
auto r = iface_->sd_bus_message_new_method_call( bus_.get()
|
||||||
, &sdbusMsg
|
, &sdbusMsg
|
||||||
, destination.c_str()
|
, destination.empty() ? nullptr : destination.c_str()
|
||||||
, objectPath.c_str()
|
, objectPath.c_str()
|
||||||
, interfaceName.c_str()
|
, interfaceName.c_str()
|
||||||
, methodName.c_str() );
|
, methodName.c_str() );
|
||||||
@@ -527,6 +542,7 @@ std::string Connection::composeSignalMatchFilter( const std::string &sender
|
|||||||
std::string filter;
|
std::string filter;
|
||||||
|
|
||||||
filter += "type='signal',";
|
filter += "type='signal',";
|
||||||
|
if (!sender.empty())
|
||||||
filter += "sender='" + sender + "',";
|
filter += "sender='" + sender + "',";
|
||||||
filter += "interface='" + interfaceName + "',";
|
filter += "interface='" + interfaceName + "',";
|
||||||
filter += "member='" + signalName + "',";
|
filter += "member='" + signalName + "',";
|
||||||
@@ -661,7 +677,6 @@ std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string
|
|||||||
std::unique_ptr<sdbus::IConnection> createSessionBusConnectionWithAddress(const std::string &address)
|
std::unique_ptr<sdbus::IConnection> createSessionBusConnectionWithAddress(const std::string &address)
|
||||||
{
|
{
|
||||||
auto interface = std::make_unique<sdbus::internal::SdBus>();
|
auto interface = std::make_unique<sdbus::internal::SdBus>();
|
||||||
assert(interface != nullptr);
|
|
||||||
return std::make_unique<sdbus::internal::Connection>(std::move(interface), Connection::custom_session_bus, address);
|
return std::make_unique<sdbus::internal::Connection>(std::move(interface), Connection::custom_session_bus, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -671,4 +686,22 @@ std::unique_ptr<sdbus::IConnection> createRemoteSystemBusConnection(const std::s
|
|||||||
return std::make_unique<sdbus::internal::Connection>(std::move(interface), Connection::remote_system_bus, host);
|
return std::make_unique<sdbus::internal::Connection>(std::move(interface), Connection::remote_system_bus, host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<sdbus::IConnection> createDirectBusConnection(const std::string& address)
|
||||||
|
{
|
||||||
|
auto interface = std::make_unique<sdbus::internal::SdBus>();
|
||||||
|
return std::make_unique<sdbus::internal::Connection>(std::move(interface), Connection::private_bus, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<sdbus::IConnection> createDirectBusConnection(int fd)
|
||||||
|
{
|
||||||
|
auto interface = std::make_unique<sdbus::internal::SdBus>();
|
||||||
|
return std::make_unique<sdbus::internal::Connection>(std::move(interface), Connection::private_bus, fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<sdbus::IConnection> createServerBus(int fd)
|
||||||
|
{
|
||||||
|
auto interface = std::make_unique<sdbus::internal::SdBus>();
|
||||||
|
return std::make_unique<sdbus::internal::Connection>(std::move(interface), Connection::server_bus, fd);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sdbus
|
} // namespace sdbus
|
||||||
|
@@ -57,6 +57,10 @@ namespace sdbus::internal {
|
|||||||
inline static constexpr custom_session_bus_t custom_session_bus{};
|
inline static constexpr custom_session_bus_t custom_session_bus{};
|
||||||
struct remote_system_bus_t{};
|
struct remote_system_bus_t{};
|
||||||
inline static constexpr remote_system_bus_t remote_system_bus{};
|
inline static constexpr remote_system_bus_t remote_system_bus{};
|
||||||
|
struct private_bus_t{};
|
||||||
|
inline static constexpr private_bus_t private_bus{};
|
||||||
|
struct server_bus_t{};
|
||||||
|
inline static constexpr server_bus_t server_bus{};
|
||||||
struct pseudo_bus_t{}; // A bus connection that is not really established with D-Bus daemon
|
struct pseudo_bus_t{}; // A bus connection that is not really established with D-Bus daemon
|
||||||
inline static constexpr pseudo_bus_t pseudo_bus{};
|
inline static constexpr pseudo_bus_t pseudo_bus{};
|
||||||
|
|
||||||
@@ -65,6 +69,9 @@ namespace sdbus::internal {
|
|||||||
Connection(std::unique_ptr<ISdBus>&& interface, session_bus_t);
|
Connection(std::unique_ptr<ISdBus>&& interface, session_bus_t);
|
||||||
Connection(std::unique_ptr<ISdBus>&& interface, custom_session_bus_t, const std::string& address);
|
Connection(std::unique_ptr<ISdBus>&& interface, custom_session_bus_t, const std::string& address);
|
||||||
Connection(std::unique_ptr<ISdBus>&& interface, remote_system_bus_t, const std::string& host);
|
Connection(std::unique_ptr<ISdBus>&& interface, remote_system_bus_t, const std::string& host);
|
||||||
|
Connection(std::unique_ptr<ISdBus>&& interface, private_bus_t, const std::string& address);
|
||||||
|
Connection(std::unique_ptr<ISdBus>&& interface, private_bus_t, int fd);
|
||||||
|
Connection(std::unique_ptr<ISdBus>&& interface, server_bus_t, int fd);
|
||||||
Connection(std::unique_ptr<ISdBus>&& interface, pseudo_bus_t);
|
Connection(std::unique_ptr<ISdBus>&& interface, pseudo_bus_t);
|
||||||
~Connection() override;
|
~Connection() override;
|
||||||
|
|
||||||
|
@@ -71,6 +71,9 @@ namespace sdbus::internal {
|
|||||||
virtual int sd_bus_open_user(sd_bus **ret) = 0;
|
virtual int sd_bus_open_user(sd_bus **ret) = 0;
|
||||||
virtual int sd_bus_open_user_with_address(sd_bus **ret, const char* address) = 0;
|
virtual int sd_bus_open_user_with_address(sd_bus **ret, const char* address) = 0;
|
||||||
virtual int sd_bus_open_system_remote(sd_bus **ret, const char* host) = 0;
|
virtual int sd_bus_open_system_remote(sd_bus **ret, const char* host) = 0;
|
||||||
|
virtual int sd_bus_open_direct(sd_bus **ret, const char* address) = 0;
|
||||||
|
virtual int sd_bus_open_direct(sd_bus **ret, int fd) = 0;
|
||||||
|
virtual int sd_bus_open_server(sd_bus **ret, int fd) = 0;
|
||||||
virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) = 0;
|
virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) = 0;
|
||||||
virtual int sd_bus_release_name(sd_bus *bus, const char *name) = 0;
|
virtual int sd_bus_release_name(sd_bus *bus, const char *name) = 0;
|
||||||
virtual int sd_bus_get_unique_name(sd_bus *bus, const char **name) = 0;
|
virtual int sd_bus_get_unique_name(sd_bus *bus, const char **name) = 0;
|
||||||
|
@@ -194,26 +194,98 @@ int SdBus::sd_bus_open_user_with_address(sd_bus **ret, const char* address)
|
|||||||
{
|
{
|
||||||
sd_bus* bus = nullptr;
|
sd_bus* bus = nullptr;
|
||||||
|
|
||||||
int r = sd_bus_new(&bus);
|
int r = ::sd_bus_new(&bus);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = sd_bus_set_address(bus, address);
|
r = ::sd_bus_set_address(bus, address);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = sd_bus_set_bus_client(bus, true);
|
r = ::sd_bus_set_bus_client(bus, true);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
// Copying behavior from
|
// Copying behavior from
|
||||||
// https://github.com/systemd/systemd/blob/fee6441601c979165ebcbb35472036439f8dad5f/src/libsystemd/sd-bus/sd-bus.c#L1381
|
// https://github.com/systemd/systemd/blob/fee6441601c979165ebcbb35472036439f8dad5f/src/libsystemd/sd-bus/sd-bus.c#L1381
|
||||||
// Here, we make the bus as trusted
|
// Here, we make the bus as trusted
|
||||||
r = sd_bus_set_trusted(bus, true);
|
r = ::sd_bus_set_trusted(bus, true);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = sd_bus_start(bus);
|
r = ::sd_bus_start(bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
*ret = bus;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SdBus::sd_bus_open_direct(sd_bus **ret, const char* address)
|
||||||
|
{
|
||||||
|
sd_bus* bus = nullptr;
|
||||||
|
|
||||||
|
int r = ::sd_bus_new(&bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = ::sd_bus_set_address(bus, address);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = ::sd_bus_start(bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
*ret = bus;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SdBus::sd_bus_open_direct(sd_bus **ret, int fd)
|
||||||
|
{
|
||||||
|
sd_bus* bus = nullptr;
|
||||||
|
|
||||||
|
int r = ::sd_bus_new(&bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = ::sd_bus_set_fd(bus, fd, fd);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = ::sd_bus_start(bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
*ret = bus;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SdBus::sd_bus_open_server(sd_bus **ret, int fd)
|
||||||
|
{
|
||||||
|
sd_bus* bus = nullptr;
|
||||||
|
|
||||||
|
int r = ::sd_bus_new(&bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = ::sd_bus_set_fd(bus, fd, fd);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
sd_id128_t id;
|
||||||
|
r = ::sd_id128_randomize(&id);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = ::sd_bus_set_server(bus, true, id);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = ::sd_bus_start(bus);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@@ -270,7 +342,7 @@ int SdBus::sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match,
|
|||||||
{
|
{
|
||||||
std::lock_guard lock(sdbusMutex_);
|
std::lock_guard lock(sdbusMutex_);
|
||||||
|
|
||||||
return :: sd_bus_add_match(bus, slot, match, callback, userdata);
|
return ::sd_bus_add_match(bus, slot, match, callback, userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
sd_bus_slot* SdBus::sd_bus_slot_unref(sd_bus_slot *slot)
|
sd_bus_slot* SdBus::sd_bus_slot_unref(sd_bus_slot *slot)
|
||||||
|
@@ -63,6 +63,9 @@ public:
|
|||||||
virtual int sd_bus_open_user(sd_bus **ret) override;
|
virtual int sd_bus_open_user(sd_bus **ret) override;
|
||||||
virtual int sd_bus_open_user_with_address(sd_bus **ret, const char* address) override;
|
virtual int sd_bus_open_user_with_address(sd_bus **ret, const char* address) override;
|
||||||
virtual int sd_bus_open_system_remote(sd_bus **ret, const char* hsot) override;
|
virtual int sd_bus_open_system_remote(sd_bus **ret, const char* hsot) override;
|
||||||
|
virtual int sd_bus_open_direct(sd_bus **ret, const char* address) override;
|
||||||
|
virtual int sd_bus_open_direct(sd_bus **ret, int fd) override;
|
||||||
|
virtual int sd_bus_open_server(sd_bus **ret, int fd) override;
|
||||||
virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) override;
|
virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) override;
|
||||||
virtual int sd_bus_release_name(sd_bus *bus, const char *name) override;
|
virtual int sd_bus_release_name(sd_bus *bus, const char *name) override;
|
||||||
virtual int sd_bus_get_unique_name(sd_bus *bus, const char **name) override;
|
virtual int sd_bus_get_unique_name(sd_bus *bus, const char **name) override;
|
||||||
|
@@ -38,7 +38,7 @@
|
|||||||
SDBUS_THROW_ERROR_IF(!sd_bus_interface_name_is_valid(_NAME.c_str()), "Invalid interface name '" + _NAME + "' provided", EINVAL) \
|
SDBUS_THROW_ERROR_IF(!sd_bus_interface_name_is_valid(_NAME.c_str()), "Invalid interface name '" + _NAME + "' provided", EINVAL) \
|
||||||
/**/
|
/**/
|
||||||
#define SDBUS_CHECK_SERVICE_NAME(_NAME) \
|
#define SDBUS_CHECK_SERVICE_NAME(_NAME) \
|
||||||
SDBUS_THROW_ERROR_IF(!sd_bus_service_name_is_valid(_NAME.c_str()), "Invalid service name '" + _NAME + "' provided", EINVAL) \
|
SDBUS_THROW_ERROR_IF(!_NAME.empty() && !sd_bus_service_name_is_valid(_NAME.c_str()), "Invalid service name '" + _NAME + "' provided", EINVAL) \
|
||||||
/**/
|
/**/
|
||||||
#define SDBUS_CHECK_MEMBER_NAME(_NAME) \
|
#define SDBUS_CHECK_MEMBER_NAME(_NAME) \
|
||||||
SDBUS_THROW_ERROR_IF(!sd_bus_member_name_is_valid(_NAME.c_str()), "Invalid member name '" + _NAME + "' provided", EINVAL) \
|
SDBUS_THROW_ERROR_IF(!sd_bus_member_name_is_valid(_NAME.c_str()), "Invalid member name '" + _NAME + "' provided", EINVAL) \
|
||||||
|
@@ -46,6 +46,7 @@ using namespace std::chrono_literals;
|
|||||||
using namespace sdbus::test;
|
using namespace sdbus::test;
|
||||||
|
|
||||||
using AConnection = TestFixture;
|
using AConnection = TestFixture;
|
||||||
|
using ADirectConnection = TestFixtureWithDirectConnection;
|
||||||
|
|
||||||
/*-------------------------------------*/
|
/*-------------------------------------*/
|
||||||
/* -- TEST CASES -- */
|
/* -- TEST CASES -- */
|
||||||
@@ -144,3 +145,14 @@ TEST_F(AConnection, WillNotPassToMatchCallbackMessagesThatDoNotMatchTheRule)
|
|||||||
ASSERT_TRUE(waitUntil([&](){ return numberOfMatchingMessages == 2; }));
|
ASSERT_TRUE(waitUntil([&](){ return numberOfMatchingMessages == 2; }));
|
||||||
ASSERT_FALSE(waitUntil([&](){ return numberOfMatchingMessages > 2; }, 1s));
|
ASSERT_FALSE(waitUntil([&](){ return numberOfMatchingMessages > 2; }, 1s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A simple direct connection test similar in nature to https://github.com/systemd/systemd/blob/main/src/libsystemd/sd-bus/test-bus-server.c
|
||||||
|
TEST_F(ADirectConnection, CanBeUsedBetweenClientAndServer)
|
||||||
|
{
|
||||||
|
auto val = m_proxy->sumArrayItems({1, 7}, {2, 3, 4});
|
||||||
|
m_adaptor->emitSimpleSignal();
|
||||||
|
|
||||||
|
// Make sure method call passes and emitted signal is received
|
||||||
|
ASSERT_THAT(val, Eq(1 + 7 + 2 + 3 + 4));
|
||||||
|
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
|
||||||
|
}
|
||||||
|
@@ -30,14 +30,17 @@
|
|||||||
#include "sdbus-c++/Types.h"
|
#include "sdbus-c++/Types.h"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
namespace sdbus { namespace test {
|
namespace sdbus { namespace test {
|
||||||
|
|
||||||
const std::string INTERFACE_NAME{"org.sdbuscpp.integrationtests"};
|
const std::string INTERFACE_NAME{"org.sdbuscpp.integrationtests"};
|
||||||
const std::string BUS_NAME = INTERFACE_NAME;
|
const std::string BUS_NAME = INTERFACE_NAME;
|
||||||
|
const std::string EMPTY_DESTINATION;
|
||||||
const std::string MANAGER_PATH {"/org/sdbuscpp/integrationtests"};
|
const std::string MANAGER_PATH {"/org/sdbuscpp/integrationtests"};
|
||||||
const std::string OBJECT_PATH {"/org/sdbuscpp/integrationtests/ObjectA1"};
|
const std::string OBJECT_PATH {"/org/sdbuscpp/integrationtests/ObjectA1"};
|
||||||
const std::string OBJECT_PATH_2{"/org/sdbuscpp/integrationtests/ObjectB1"};
|
const std::string OBJECT_PATH_2{"/org/sdbuscpp/integrationtests/ObjectB1"};
|
||||||
|
const std::string DIRECT_CONNECTION_SOCKET_PATH{std::filesystem::temp_directory_path() / "sdbus-cpp-direct-connection-test"};
|
||||||
|
|
||||||
constexpr const uint8_t UINT8_VALUE{1};
|
constexpr const uint8_t UINT8_VALUE{1};
|
||||||
constexpr const int16_t INT16_VALUE{21};
|
constexpr const int16_t INT16_VALUE{21};
|
||||||
|
@@ -39,6 +39,11 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
namespace sdbus { namespace test {
|
namespace sdbus { namespace test {
|
||||||
|
|
||||||
class TestFixture : public ::testing::Test
|
class TestFixture : public ::testing::Test
|
||||||
@@ -59,28 +64,6 @@ public:
|
|||||||
s_proxyConnection->leaveEventLoop();
|
s_proxyConnection->leaveEventLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename _Fnc>
|
|
||||||
static bool waitUntil(_Fnc&& fnc, std::chrono::milliseconds timeout = std::chrono::seconds(5))
|
|
||||||
{
|
|
||||||
using namespace std::chrono_literals;
|
|
||||||
|
|
||||||
std::chrono::milliseconds elapsed{};
|
|
||||||
std::chrono::milliseconds step{5ms};
|
|
||||||
do {
|
|
||||||
std::this_thread::sleep_for(step);
|
|
||||||
elapsed += step;
|
|
||||||
if (elapsed > timeout)
|
|
||||||
return false;
|
|
||||||
} while (!fnc());
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool waitUntil(std::atomic<bool>& flag, std::chrono::milliseconds timeout = std::chrono::seconds(5))
|
|
||||||
{
|
|
||||||
return waitUntil([&flag]() -> bool { return flag; }, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetUp() override
|
void SetUp() override
|
||||||
{
|
{
|
||||||
@@ -106,6 +89,101 @@ public:
|
|||||||
std::unique_ptr<TestProxy> m_proxy;
|
std::unique_ptr<TestProxy> m_proxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TestFixtureWithDirectConnection : public ::testing::Test
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
void SetUp() override
|
||||||
|
{
|
||||||
|
int sock = openUnixSocket();
|
||||||
|
createClientAndServerConnections(sock);
|
||||||
|
createAdaptorAndProxyObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override
|
||||||
|
{
|
||||||
|
m_proxy.reset();
|
||||||
|
m_adaptor.reset();
|
||||||
|
m_proxyConnection->leaveEventLoop();
|
||||||
|
m_adaptorConnection->leaveEventLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int openUnixSocket()
|
||||||
|
{
|
||||||
|
int sock = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
|
||||||
|
assert(sock >= 0);
|
||||||
|
|
||||||
|
sockaddr_un sa;
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sa.sun_family = AF_UNIX;
|
||||||
|
snprintf(sa.sun_path, sizeof(sa.sun_path), "%s", DIRECT_CONNECTION_SOCKET_PATH.c_str());
|
||||||
|
|
||||||
|
unlink(DIRECT_CONNECTION_SOCKET_PATH.c_str());
|
||||||
|
|
||||||
|
umask(0000);
|
||||||
|
[[maybe_unused]] int r = bind(sock, (const sockaddr*) &sa, sizeof(sa.sun_path));
|
||||||
|
assert(r >= 0);
|
||||||
|
|
||||||
|
r = listen(sock, 5);
|
||||||
|
assert(r >= 0);
|
||||||
|
|
||||||
|
return sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void createClientAndServerConnections(int sock)
|
||||||
|
{
|
||||||
|
std::thread t([&]()
|
||||||
|
{
|
||||||
|
auto fd = accept4(sock, NULL, NULL, /*SOCK_NONBLOCK|*/SOCK_CLOEXEC);
|
||||||
|
m_adaptorConnection = sdbus::createServerBus(fd);
|
||||||
|
// This is necessary so that createDirectBusConnection() below does not block
|
||||||
|
m_adaptorConnection->enterEventLoopAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_proxyConnection = sdbus::createDirectBusConnection("unix:path=" + DIRECT_CONNECTION_SOCKET_PATH);
|
||||||
|
m_proxyConnection->enterEventLoopAsync();
|
||||||
|
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void createAdaptorAndProxyObjects()
|
||||||
|
{
|
||||||
|
assert(m_adaptorConnection != nullptr);
|
||||||
|
assert(m_proxyConnection != nullptr);
|
||||||
|
|
||||||
|
m_adaptor = std::make_unique<TestAdaptor>(*m_adaptorConnection, OBJECT_PATH);
|
||||||
|
// Destination parameter can be empty in case of direct connections
|
||||||
|
m_proxy = std::make_unique<TestProxy>(*m_proxyConnection, EMPTY_DESTINATION, OBJECT_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::unique_ptr<sdbus::IConnection> m_adaptorConnection;
|
||||||
|
std::unique_ptr<sdbus::IConnection> m_proxyConnection;
|
||||||
|
std::unique_ptr<TestAdaptor> m_adaptor;
|
||||||
|
std::unique_ptr<TestProxy> m_proxy;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename _Fnc>
|
||||||
|
inline bool waitUntil(_Fnc&& fnc, std::chrono::milliseconds timeout = std::chrono::seconds(5))
|
||||||
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
std::chrono::milliseconds elapsed{};
|
||||||
|
std::chrono::milliseconds step{5ms};
|
||||||
|
do {
|
||||||
|
std::this_thread::sleep_for(step);
|
||||||
|
elapsed += step;
|
||||||
|
if (elapsed > timeout)
|
||||||
|
return false;
|
||||||
|
} while (!fnc());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool waitUntil(std::atomic<bool>& flag, std::chrono::milliseconds timeout = std::chrono::seconds(5))
|
||||||
|
{
|
||||||
|
return waitUntil([&flag]() -> bool { return flag; }, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTFIXTURE_H_ */
|
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTFIXTURE_H_ */
|
||||||
|
@@ -62,6 +62,9 @@ public:
|
|||||||
MOCK_METHOD1(sd_bus_open_user, int(sd_bus **ret));
|
MOCK_METHOD1(sd_bus_open_user, int(sd_bus **ret));
|
||||||
MOCK_METHOD2(sd_bus_open_user_with_address, int(sd_bus **ret, const char* address));
|
MOCK_METHOD2(sd_bus_open_user_with_address, int(sd_bus **ret, const char* address));
|
||||||
MOCK_METHOD2(sd_bus_open_system_remote, int(sd_bus **ret, const char *host));
|
MOCK_METHOD2(sd_bus_open_system_remote, int(sd_bus **ret, const char *host));
|
||||||
|
MOCK_METHOD2(sd_bus_open_direct, int(sd_bus **ret, const char* address));
|
||||||
|
MOCK_METHOD2(sd_bus_open_direct, int(sd_bus **ret, int fd));
|
||||||
|
MOCK_METHOD2(sd_bus_open_server, int(sd_bus **ret, int fd));
|
||||||
MOCK_METHOD3(sd_bus_request_name, int(sd_bus *bus, const char *name, uint64_t flags));
|
MOCK_METHOD3(sd_bus_request_name, int(sd_bus *bus, const char *name, uint64_t flags));
|
||||||
MOCK_METHOD2(sd_bus_release_name, int(sd_bus *bus, const char *name));
|
MOCK_METHOD2(sd_bus_release_name, int(sd_bus *bus, const char *name));
|
||||||
MOCK_METHOD2(sd_bus_get_unique_name, int(sd_bus *bus, const char **name));
|
MOCK_METHOD2(sd_bus_get_unique_name, int(sd_bus *bus, const char **name));
|
||||||
|
Reference in New Issue
Block a user