Compare commits

...
10 Commits
28 changed files with 556 additions and 166 deletions
+2 -2
View File
@@ -4,7 +4,7 @@
cmake_minimum_required(VERSION 3.6)
project(sdbus-c++ VERSION 0.7.2 LANGUAGES C CXX)
project(sdbus-c++ VERSION 0.7.4 LANGUAGES C CXX)
include(GNUInstallDirs) # Installation directories for `install` command and pkgconfig file
@@ -98,7 +98,7 @@ option(BUILD_SHARED_LIBS "Build shared libraries (.so) instead of static ones (.
# Having an object target allows unit tests to reuse already built sources without re-building
add_library(sdbus-c++-objlib OBJECT ${SDBUSCPP_SRCS})
target_compile_definitions(sdbus-c++-objlib PRIVATE BUILDLIB=1)
target_compile_definitions(sdbus-c++-objlib PRIVATE BUILD_LIB=1 LIBSYSTEMD_VERSION=${SYSTEMD_VERSION})
target_include_directories(sdbus-c++-objlib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<BUILD_INTERFACE:${SYSTEMD_INCLUDE_DIRS}>)
+10
View File
@@ -114,3 +114,13 @@ v0.7.1
v0.7.2
- Rewrite UnixFd implementation from plain UnixFd struct to full-ownership-semantics UnixFd class
v0.7.3
- Add ability to integrate with external event loops
- Add getSenderName() method to Message
- Skip GetMachineId integration test case when /etc/machine-id is not available
v0.7.4
- Add support for custom timeout of D-Bus method calls
- Add support for opening a connection to a remote system bus using ssh
- Internal refactoring: Use tag dispatching to construct various types of Connection
+2
View File
@@ -50,6 +50,8 @@ set(SYSTEMD_INCLUDE_DIRS ${SOURCE_DIR}/src)
ExternalProject_Get_property(LibsystemdBuildProject BINARY_DIR)
set(SYSTEMD_LIBRARY_DIRS ${BINARY_DIR})
set(SYSTEMD_VERSION ${LIBSYSTEMD_VERSION})
add_library(Systemd::Libsystemd STATIC IMPORTED)
set_target_properties(Systemd::Libsystemd PROPERTIES IMPORTED_LOCATION ${SYSTEMD_LIBRARY_DIRS}/libsystemd.a)
set(SYSTEMD_LIBRARIES Systemd::Libsystemd ${CAP_LIBRARIES} ${GLIBC_RT_LIBRARY} ${MOUNT_LIBRARIES})
+2
View File
@@ -106,6 +106,8 @@ There are two recipes:
* One for sdbus-c++ itself. It detects whether systemd feature is turned on in the poky linux configuration. If so, it simply depends on systemd and makes use of libsystemd shared library available in the target system. Otherwise it automatically downloads and builds libsystemd static library and makes it an opaque part of sdbus-c++ shared library. The recipe also supports ptest.
* One for sdbus-c++ native tools, namely sdbus-c++ code generator to generate C++ adaptor and proxy binding classes.
Tip: If you get `ERROR: Program or command 'getent' not found or not executable` when building sdbus-c++ in Yocto, please make sure you've added `getent` to `HOSTTOOLS`. For example, you can add `HOSTTOOLS_NONFATAL += "getent"` into your local.conf file.
Header files and namespaces
---------------------------
+10
View File
@@ -32,6 +32,8 @@
#include <sdbus-c++/Flags.h>
#include <string>
#include <type_traits>
#include <chrono>
#include <cstdint>
// Forward declarations
namespace sdbus {
@@ -164,6 +166,9 @@ namespace sdbus {
~MethodInvoker() noexcept(false);
MethodInvoker& onInterface(const std::string& interfaceName);
MethodInvoker& withTimeout(uint64_t usec);
template <typename _Rep, typename _Period>
MethodInvoker& withTimeout(const std::chrono::duration<_Rep, _Period>& timeout);
template <typename... _Args> MethodInvoker& withArguments(_Args&&... args);
template <typename... _Args> void storeResultsTo(_Args&... args);
@@ -172,6 +177,7 @@ namespace sdbus {
private:
IProxy& proxy_;
const std::string& methodName_;
uint64_t timeout_{};
MethodCall method_;
int exceptions_{}; // Number of active exceptions when MethodInvoker is constructed
bool methodCalled_{};
@@ -182,12 +188,16 @@ namespace sdbus {
public:
AsyncMethodInvoker(IProxy& proxy, const std::string& methodName);
AsyncMethodInvoker& onInterface(const std::string& interfaceName);
AsyncMethodInvoker& withTimeout(uint64_t usec);
template <typename _Rep, typename _Period>
AsyncMethodInvoker& withTimeout(const std::chrono::duration<_Rep, _Period>& timeout);
template <typename... _Args> AsyncMethodInvoker& withArguments(_Args&&... args);
template <typename _Function> void uponReplyInvoke(_Function&& callback);
private:
IProxy& proxy_;
const std::string& methodName_;
uint64_t timeout_{};
AsyncMethodCall method_;
};
+64 -3
View File
@@ -40,6 +40,10 @@
namespace sdbus {
/*** ----------------- ***/
/*** MethodRegistrator ***/
/*** ----------------- ***/
// Moved into the library to isolate from C++17 dependency
/*
inline MethodRegistrator::MethodRegistrator(IObject& object, const std::string& methodName)
@@ -149,6 +153,9 @@ namespace sdbus {
return *this;
}
/*** ----------------- ***/
/*** SignalRegistrator ***/
/*** ----------------- ***/
// Moved into the library to isolate from C++17 dependency
/*
@@ -203,6 +210,9 @@ namespace sdbus {
return *this;
}
/*** ------------------- ***/
/*** PropertyRegistrator ***/
/*** ------------------- ***/
// Moved into the library to isolate from C++17 dependency
/*
@@ -309,6 +319,9 @@ namespace sdbus {
return *this;
}
/*** -------------------- ***/
/*** InterfaceFlagsSetter ***/
/*** -------------------- ***/
// Moved into the library to isolate from C++17 dependency
/*
@@ -369,6 +382,9 @@ namespace sdbus {
return *this;
}
/*** ------------- ***/
/*** SignalEmitter ***/
/*** ------------- ***/
// Moved into the library to isolate from C++17 dependency
/*
@@ -416,6 +432,9 @@ namespace sdbus {
detail::serialize_pack(signal_, std::forward<_Args>(args)...);
}
/*** ------------- ***/
/*** MethodInvoker ***/
/*** ------------- ***/
// Moved into the library to isolate from C++17 dependency
/*
@@ -456,6 +475,20 @@ namespace sdbus {
return *this;
}
inline MethodInvoker& MethodInvoker::withTimeout(uint64_t usec)
{
timeout_ = usec;
return *this;
}
template <typename _Rep, typename _Period>
inline MethodInvoker& MethodInvoker::withTimeout(const std::chrono::duration<_Rep, _Period>& timeout)
{
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
return withTimeout(microsecs.count());
}
template <typename... _Args>
inline MethodInvoker& MethodInvoker::withArguments(_Args&&... args)
{
@@ -471,7 +504,7 @@ namespace sdbus {
{
SDBUS_THROW_ERROR_IF(!method_.isValid(), "DBus interface not specified when calling a DBus method", EINVAL);
auto reply = proxy_.callMethod(method_);
auto reply = proxy_.callMethod(method_, timeout_);
methodCalled_ = true;
detail::deserialize_pack(reply, args...);
@@ -484,6 +517,9 @@ namespace sdbus {
method_.dontExpectReply();
}
/*** ------------------ ***/
/*** AsyncMethodInvoker ***/
/*** ------------------ ***/
inline AsyncMethodInvoker::AsyncMethodInvoker(IProxy& proxy, const std::string& methodName)
: proxy_(proxy)
@@ -498,6 +534,20 @@ namespace sdbus {
return *this;
}
inline AsyncMethodInvoker& AsyncMethodInvoker::withTimeout(uint64_t usec)
{
timeout_ = usec;
return *this;
}
template <typename _Rep, typename _Period>
inline AsyncMethodInvoker& AsyncMethodInvoker::withTimeout(const std::chrono::duration<_Rep, _Period>& timeout)
{
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
return withTimeout(microsecs.count());
}
template <typename... _Args>
inline AsyncMethodInvoker& AsyncMethodInvoker::withArguments(_Args&&... args)
{
@@ -513,7 +563,7 @@ namespace sdbus {
{
SDBUS_THROW_ERROR_IF(!method_.isValid(), "DBus interface not specified when calling a DBus method", EINVAL);
proxy_.callMethod(method_, [callback = std::forward<_Function>(callback)](MethodReply& reply, const Error* error)
auto asyncReplyHandler = [callback = std::forward<_Function>(callback)](MethodReply& reply, const Error* error)
{
// Create a tuple of callback input arguments' types, which will be used
// as a storage for the argument values deserialized from the message.
@@ -525,9 +575,14 @@ namespace sdbus {
// Invoke callback with input arguments from the tuple.
sdbus::apply(callback, error, args); // TODO: Use std::apply when switching to full C++17 support
});
};
proxy_.callMethod(method_, std::move(asyncReplyHandler), timeout_);
}
/*** ---------------- ***/
/*** SignalSubscriber ***/
/*** ---------------- ***/
inline SignalSubscriber::SignalSubscriber(IProxy& proxy, const std::string& signalName)
: proxy_(proxy)
@@ -563,6 +618,9 @@ namespace sdbus {
});
}
/*** -------------- ***/
/*** PropertyGetter ***/
/*** -------------- ***/
inline PropertyGetter::PropertyGetter(IProxy& proxy, const std::string& propertyName)
: proxy_(proxy)
@@ -581,6 +639,9 @@ namespace sdbus {
return var;
}
/*** -------------- ***/
/*** PropertySetter ***/
/*** -------------- ***/
inline PropertySetter::PropertySetter(IProxy& proxy, const std::string& propertyName)
: proxy_(proxy)
+91 -1
View File
@@ -27,9 +27,10 @@
#ifndef SDBUS_CXX_ICONNECTION_H_
#define SDBUS_CXX_ICONNECTION_H_
//#include <cstdint>
#include <string>
#include <memory>
#include <chrono>
#include <cstdint>
namespace sdbus {
@@ -46,6 +47,13 @@ namespace sdbus {
class IConnection
{
public:
struct PollData
{
int fd;
short int events;
uint64_t timeout_usec;
};
virtual ~IConnection() = default;
/*!
@@ -103,8 +111,81 @@ namespace sdbus {
* @throws sdbus::Error in case of failure
*/
virtual void addObjectManager(const std::string& objectPath) = 0;
/*!
* @brief Returns parameters you can pass to poll
*
* To integrate sdbus with your app's own custom event handling system
* (without the requirement of an extra thread), you can use this
* method to query which file descriptors, poll events and timeouts you
* should add to your app's poll call in your main event loop. If these
* file descriptors signal, then you should call processPendingRequest
* to process the event. This means that all of sdbus's callbacks will
* arrive on your app's main event thread (opposed to on a thread created
* by sdbus-c++). If you are unsure what this all means then use
* enterProcessingLoop() or enterProcessingLoopAsync() instead.
*
* To integrate sdbus-c++ into a gtk app, pass the file descriptor returned
* by this method to g_main_context_add_poll.
*
* @throws sdbus::Error in case of failure
*/
virtual PollData getProcessLoopPollData() = 0;
/*!
* @brief Process a pending request
*
* Processes a single dbus event. All of sdbus-c++'s callbacks will be called
* from within this method. This method should ONLY be used in conjuction
* with getProcessLoopPollData(). enterProcessingLoop() and
* enterProcessingLoopAsync() will call this method for you, so there is no
* need to call it when using these. If you are unsure what this all means then
* don't use this method.
*
* @returns true if an event was processed
* @throws sdbus::Error in case of failure
*/
virtual bool processPendingRequest() = 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 <typename _Rep, typename _Period>
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
*/
virtual uint64_t getMethodCallTimeout() const = 0;
};
template <typename _Rep, typename _Period>
inline void IConnection::setMethodCallTimeout(const std::chrono::duration<_Rep, _Period>& timeout)
{
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
return setMethodCallTimeout(microsecs.count());
}
/*!
* @brief Creates/opens D-Bus system connection
*
@@ -162,6 +243,15 @@ namespace sdbus {
*/
std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name);
/*!
* @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
*/
std::unique_ptr<sdbus::IConnection> createRemoteSystemBusConnection(const std::string& host);
}
#endif /* SDBUS_CXX_ICONNECTION_H_ */
+2
View File
@@ -387,6 +387,8 @@ namespace sdbus {
SignalEmitter emitSignal(const std::string& signalName);
};
// Out-of-line member definitions
inline MethodRegistrator IObject::registerMethod(const std::string& methodName)
{
return MethodRegistrator(*this, methodName);
+33 -2
View File
@@ -31,6 +31,7 @@
#include <string>
#include <memory>
#include <functional>
#include <chrono>
// Forward declarations
namespace sdbus {
@@ -95,6 +96,7 @@ namespace sdbus {
* @brief Calls method on the proxied D-Bus object
*
* @param[in] message Message representing a method call
* @param[in] timeout Timeout for dbus call in microseconds
* @return A method reply message
*
* Normally, the call is blocking, i.e. it waits for the remote method to finish with either
@@ -108,13 +110,20 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
virtual MethodReply callMethod(const MethodCall& message) = 0;
virtual MethodReply callMethod(const MethodCall& message, uint64_t timeout = 0) = 0;
/*!
* @copydoc IProxy::callMethod(const MethodCall&,uint64_t)
*/
template <typename _Rep, typename _Period>
MethodReply callMethod(const MethodCall& message, const std::chrono::duration<_Rep, _Period>& timeout);
/*!
* @brief Calls method on the proxied D-Bus object asynchronously
*
* @param[in] message Message representing an async method call
* @param[in] asyncReplyCallback Handler for the async reply
* @param[in] timeout Timeout for dbus call in microseconds
*
* The call is non-blocking. It doesn't wait for the reply. Once the reply arrives,
* the provided async reply handler will get invoked from the context of the connection
@@ -124,7 +133,13 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
virtual void callMethod(const AsyncMethodCall& message, async_reply_handler asyncReplyCallback) = 0;
virtual void callMethod(const AsyncMethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout = 0) = 0;
/*!
* @copydoc IProxy::callMethod(const AsyncMethodCall&,async_reply_handler,uint64_t)
*/
template <typename _Rep, typename _Period>
void callMethod(const AsyncMethodCall& message, async_reply_handler asyncReplyCallback, const std::chrono::duration<_Rep, _Period>& timeout);
/*!
* @brief Registers a handler for the desired signal emitted by the proxied D-Bus object
@@ -264,6 +279,22 @@ namespace sdbus {
PropertySetter setProperty(const std::string& propertyName);
};
// Out-of-line member definitions
template <typename _Rep, typename _Period>
inline MethodReply IProxy::callMethod(const MethodCall& message, const std::chrono::duration<_Rep, _Period>& timeout)
{
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
return callMethod(message, microsecs.count());
}
template <typename _Rep, typename _Period>
inline void IProxy::callMethod(const AsyncMethodCall& message, async_reply_handler asyncReplyCallback, const std::chrono::duration<_Rep, _Period>& timeout)
{
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
callMethod(message, std::move(asyncReplyCallback), microsecs.count());
}
inline MethodInvoker IProxy::callMethod(const std::string& methodName)
{
return MethodInvoker(*this, methodName);
+4 -3
View File
@@ -133,6 +133,7 @@ namespace sdbus {
std::string getInterfaceName() const;
std::string getMemberName() const;
std::string getSender() const;
void peekType(std::string& type, std::string& contents) const;
bool isValid() const;
bool isEmpty() const;
@@ -171,14 +172,14 @@ namespace sdbus {
public:
MethodCall() = default;
MethodReply send() const;
MethodReply send(uint64_t timeout = 0) const;
MethodReply createReply() const;
MethodReply createErrorReply(const sdbus::Error& error) const;
void dontExpectReply();
bool doesntExpectReply() const;
private:
MethodReply sendWithReply() const;
MethodReply sendWithReply(uint64_t timeout) const;
MethodReply sendWithNoReply() const;
};
@@ -192,7 +193,7 @@ namespace sdbus {
AsyncMethodCall() = default;
explicit AsyncMethodCall(MethodCall&& call) noexcept;
Slot send(void* callback, void* userData) const;
Slot send(void* callback, void* userData, uint64_t timeout = 0) const;
};
class MethodReply : public Message
+86 -61
View File
@@ -35,39 +35,33 @@
#include <poll.h>
#include <sys/eventfd.h>
namespace {
std::vector</*const */char*> to_strv(const std::vector<std::string>& strings)
{
std::vector</*const */char*> strv;
for (auto& str : strings)
strv.push_back(const_cast<char*>(str.c_str()));
strv.push_back(nullptr);
return strv;
}
}
namespace sdbus { namespace internal {
Connection::Connection(Connection::BusType type, std::unique_ptr<ISdBus>&& interface)
Connection::Connection(std::unique_ptr<ISdBus>&& interface, const BusFactory& busFactory)
: iface_(std::move(interface))
, busType_(type)
, bus_(openBus(busFactory))
{
assert(iface_ != nullptr);
}
auto bus = openBus(busType_);
bus_.reset(bus);
Connection::Connection(std::unique_ptr<ISdBus>&& interface, system_bus_t)
: Connection(std::move(interface), [this](sd_bus** bus){ return iface_->sd_bus_open_system(bus); })
{
}
finishHandshake(bus);
Connection::Connection(std::unique_ptr<ISdBus>&& interface, session_bus_t)
: Connection(std::move(interface), [this](sd_bus** bus){ return iface_->sd_bus_open_user(bus); })
{
}
loopExitFd_ = createProcessingLoopExitDescriptor();
Connection::Connection(std::unique_ptr<ISdBus>&& interface, remote_system_bus_t, const std::string& host)
: Connection(std::move(interface), [this, &host](sd_bus** bus){ return iface_->sd_bus_open_system_remote(bus, host.c_str()); })
{
}
Connection::~Connection()
{
leaveProcessingLoop();
closeProcessingLoopExitDescriptor(loopExitFd_);
}
void Connection::requestName(const std::string& name)
@@ -108,6 +102,15 @@ void Connection::leaveProcessingLoop()
joinWithProcessingLoop();
}
sdbus::IConnection::PollData Connection::getProcessLoopPollData()
{
ISdBus::PollData pollData;
auto r = iface_->sd_bus_get_poll_data(bus_.get(), &pollData);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus poll data", -r);
return {pollData.fd, pollData.events, pollData.timeout_usec};
}
const ISdBus& Connection::getSdBusInterface() const
{
return *iface_.get();
@@ -136,6 +139,24 @@ SlotPtr Connection::addObjectManager(const std::string& objectPath, void* /*dumm
return {slot, [this](void *slot){ iface_->sd_bus_slot_unref((sd_bus_slot*)slot); }};
}
void Connection::setMethodCallTimeout(uint64_t timeout)
{
auto r = iface_->sd_bus_set_method_call_timeout(bus_.get(), timeout);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set method call timeout", -r);
}
uint64_t Connection::getMethodCallTimeout() const
{
uint64_t timeout;
auto r = iface_->sd_bus_get_method_call_timeout(bus_.get(), &timeout);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get method call timeout", -r);
return timeout;
}
SlotPtr Connection::addObjectVTable( const std::string& objectPath
, const std::string& interfaceName
, const sd_bus_vtable* vtable
@@ -259,21 +280,15 @@ SlotPtr Connection::registerSignalHandler( const std::string& objectPath
return {slot, [this](void *slot){ iface_->sd_bus_slot_unref((sd_bus_slot*)slot); }};
}
sd_bus* Connection::openBus(Connection::BusType type)
Connection::BusPtr Connection::openBus(const BusFactory& busFactory)
{
sd_bus* bus{};
int r = 0;
if (type == BusType::eSystem)
r = iface_->sd_bus_open_system(&bus);
else if (type == BusType::eSession)
r = iface_->sd_bus_open_user(&bus);
else
assert(false);
int r = busFactory(&bus);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open bus", -r);
assert(bus != nullptr);
return bus;
BusPtr busPtr{bus, [this](sd_bus* bus){ return iface_->sd_bus_flush_close_unref(bus); }};
finishHandshake(busPtr.get());
return busPtr;
}
void Connection::finishHandshake(sd_bus* bus)
@@ -289,33 +304,19 @@ void Connection::finishHandshake(sd_bus* bus)
SDBUS_THROW_ERROR_IF(r < 0, "Failed to flush bus on opening", -r);
}
int Connection::createProcessingLoopExitDescriptor()
{
auto r = eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC | EFD_NONBLOCK);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create event object", -errno);
return r;
}
void Connection::closeProcessingLoopExitDescriptor(int fd)
{
close(fd);
}
void Connection::notifyProcessingLoopToExit()
{
assert(loopExitFd_ >= 0);
assert(loopExitFd_.fd >= 0);
uint64_t value = 1;
auto r = write(loopExitFd_, &value, sizeof(value));
auto r = write(loopExitFd_.fd, &value, sizeof(value));
SDBUS_THROW_ERROR_IF(r < 0, "Failed to notify processing loop", -errno);
}
void Connection::clearExitNotification()
{
uint64_t value{};
auto r = read(loopExitFd_, &value, sizeof(value));
auto r = read(loopExitFd_.fd, &value, sizeof(value));
SDBUS_THROW_ERROR_IF(r < 0, "Failed to read from the event descriptor", -errno);
}
@@ -328,11 +329,9 @@ void Connection::joinWithProcessingLoop()
bool Connection::processPendingRequest()
{
auto bus = bus_.get();
assert(bus != nullptr);
int r = iface_->sd_bus_process(bus, nullptr);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to process bus requests", -r);
return r > 0;
@@ -343,17 +342,14 @@ bool Connection::waitForNextRequest()
auto bus = bus_.get();
assert(bus != nullptr);
assert(loopExitFd_ != 0);
assert(loopExitFd_.fd != 0);
ISdBus::PollData sdbusPollData;
auto r = iface_->sd_bus_get_poll_data(bus, &sdbusPollData);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus poll data", -r);
struct pollfd fds[] = {{sdbusPollData.fd, sdbusPollData.events, 0}, {loopExitFd_, POLLIN, 0}};
auto sdbusPollData = getProcessLoopPollData();
struct pollfd fds[] = {{sdbusPollData.fd, sdbusPollData.events, 0}, {loopExitFd_.fd, POLLIN, 0}};
auto fdsCount = sizeof(fds)/sizeof(fds[0]);
auto timeout = sdbusPollData.timeout_usec == (uint64_t) -1 ? (uint64_t)-1 : (sdbusPollData.timeout_usec+999)/1000;
r = poll(fds, fdsCount, timeout);
auto r = poll(fds, fdsCount, timeout);
if (r < 0 && errno == EINTR)
return true; // Try again
@@ -383,6 +379,27 @@ std::string Connection::composeSignalMatchFilter( const std::string& objectPath
return filter;
}
std::vector</*const */char*> Connection::to_strv(const std::vector<std::string>& strings)
{
std::vector</*const */char*> strv;
for (auto& str : strings)
strv.push_back(const_cast<char*>(str.c_str()));
strv.push_back(nullptr);
return strv;
}
Connection::LoopExitEventFd::LoopExitEventFd()
{
fd = eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC | EFD_NONBLOCK);
SDBUS_THROW_ERROR_IF(fd < 0, "Failed to create event object", -errno);
}
Connection::LoopExitEventFd::~LoopExitEventFd()
{
assert(fd >= 0);
close(fd);
}
}}
namespace sdbus {
@@ -401,8 +418,8 @@ std::unique_ptr<sdbus::IConnection> createSystemBusConnection()
{
auto interface = std::make_unique<sdbus::internal::SdBus>();
assert(interface != nullptr);
return std::make_unique<sdbus::internal::Connection>( sdbus::internal::Connection::BusType::eSystem
, std::move(interface));
constexpr sdbus::internal::Connection::system_bus_t system_bus;
return std::make_unique<sdbus::internal::Connection>(std::move(interface), system_bus);
}
std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name)
@@ -416,8 +433,8 @@ std::unique_ptr<sdbus::IConnection> createSessionBusConnection()
{
auto interface = std::make_unique<sdbus::internal::SdBus>();
assert(interface != nullptr);
return std::make_unique<sdbus::internal::Connection>( sdbus::internal::Connection::BusType::eSession
, std::move(interface));
constexpr sdbus::internal::Connection::session_bus_t session_bus;
return std::make_unique<sdbus::internal::Connection>(std::move(interface), session_bus);
}
std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name)
@@ -427,4 +444,12 @@ std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string
return conn;
}
std::unique_ptr<sdbus::IConnection> createRemoteSystemBusConnection(const std::string& host)
{
auto interface = std::make_unique<sdbus::internal::SdBus>();
assert(interface != nullptr);
constexpr sdbus::internal::Connection::remote_system_bus_t remote_system_bus;
return std::make_unique<sdbus::internal::Connection>(std::move(interface), remote_system_bus, host);
}
}
+29 -17
View File
@@ -35,6 +35,8 @@
#include <systemd/sd-bus.h>
#include <memory>
#include <thread>
#include <string>
#include <vector>
namespace sdbus { namespace internal {
@@ -43,13 +45,14 @@ namespace sdbus { namespace internal {
, public sdbus::internal::IConnection // Internal, private interface
{
public:
enum class BusType
{
eSystem,
eSession
};
// Bus type tags
struct system_bus_t{};
struct session_bus_t{};
struct remote_system_bus_t{};
Connection(BusType type, std::unique_ptr<ISdBus>&& interface);
Connection(std::unique_ptr<ISdBus>&& interface, system_bus_t);
Connection(std::unique_ptr<ISdBus>&& interface, session_bus_t);
Connection(std::unique_ptr<ISdBus>&& interface, remote_system_bus_t, const std::string& host);
~Connection() override;
void requestName(const std::string& name) override;
@@ -57,10 +60,15 @@ namespace sdbus { namespace internal {
void enterProcessingLoop() override;
void enterProcessingLoopAsync() override;
void leaveProcessingLoop() override;
sdbus::IConnection::PollData getProcessLoopPollData() override;
bool processPendingRequest() override;
void addObjectManager(const std::string& objectPath) override;
SlotPtr addObjectManager(const std::string& objectPath, void* /*dummy*/) override;
void setMethodCallTimeout(uint64_t timeout) override;
uint64_t getMethodCallTimeout() const override;
const ISdBus& getSdBusInterface() const override;
ISdBus& getSdBusInterface() override;
@@ -95,11 +103,12 @@ namespace sdbus { namespace internal {
, void* userData ) override;
private:
sd_bus* openBus(Connection::BusType type);
using BusFactory = std::function<int(sd_bus**)>;
using BusPtr = std::unique_ptr<sd_bus, std::function<sd_bus*(sd_bus*)>>;
Connection(std::unique_ptr<ISdBus>&& interface, const BusFactory& busFactory);
BusPtr openBus(const std::function<int(sd_bus**)>& busFactory);
void finishHandshake(sd_bus* bus);
static int createProcessingLoopExitDescriptor();
static void closeProcessingLoopExitDescriptor(int fd);
bool processPendingRequest();
bool waitForNextRequest();
static std::string composeSignalMatchFilter( const std::string& objectPath
, const std::string& interfaceName
@@ -107,17 +116,20 @@ namespace sdbus { namespace internal {
void notifyProcessingLoopToExit();
void clearExitNotification();
void joinWithProcessingLoop();
static std::vector</*const */char*> to_strv(const std::vector<std::string>& strings);
struct LoopExitEventFd
{
LoopExitEventFd();
~LoopExitEventFd();
int fd;
};
private:
std::unique_ptr<ISdBus> iface_;
std::unique_ptr<sd_bus, std::function<sd_bus*(sd_bus*)>> bus_ {nullptr, [this](sd_bus* bus)
{
return iface_->sd_bus_flush_close_unref(bus);
}};
BusType busType_;
BusPtr bus_;
std::thread asyncLoopThread_;
int loopExitFd_{-1};
LoopExitEventFd loopExitFd_;
};
}}
+1 -1
View File
@@ -203,7 +203,7 @@ MethodInvoker::~MethodInvoker() noexcept(false) // since C++11, destructors must
// Therefore, we can allow callMethod() to throw even if we are in the destructor.
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
// to the exception thrown from here if the caller is a destructor itself.
proxy_.callMethod(method_);
proxy_.callMethod(method_, timeout_);
}
}
+6 -2
View File
@@ -42,6 +42,8 @@ namespace sdbus { namespace internal {
uint64_t timeout_usec;
};
virtual ~ISdBus() = default;
virtual sd_bus_message* sd_bus_message_ref(sd_bus_message *m) = 0;
virtual sd_bus_message* sd_bus_message_unref(sd_bus_message *m) = 0;
@@ -54,6 +56,9 @@ namespace sdbus { namespace internal {
virtual int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m) = 0;
virtual int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e) = 0;
virtual int sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec) = 0;
virtual int sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret) = 0;
virtual int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names) = 0;
virtual int sd_bus_emit_object_added(sd_bus *bus, const char *path) = 0;
virtual int sd_bus_emit_object_removed(sd_bus *bus, const char *path) = 0;
@@ -62,6 +67,7 @@ namespace sdbus { namespace internal {
virtual int sd_bus_open_user(sd_bus **ret) = 0;
virtual int sd_bus_open_system(sd_bus **ret) = 0;
virtual int sd_bus_open_system_remote(sd_bus **ret, const char* host) = 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_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) = 0;
@@ -74,8 +80,6 @@ namespace sdbus { namespace internal {
virtual int sd_bus_flush(sd_bus *bus) = 0;
virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) = 0;
virtual ~ISdBus() = default;
};
}}
+11 -6
View File
@@ -590,6 +590,11 @@ std::string Message::getMemberName() const
return sd_bus_message_get_member((sd_bus_message*)msg_);
}
std::string Message::getSender() const
{
return sd_bus_message_get_sender((sd_bus_message*)msg_);
}
void Message::peekType(std::string& type, std::string& contents) const
{
char typeSig;
@@ -623,21 +628,21 @@ bool MethodCall::doesntExpectReply() const
return r == 0;
}
MethodReply MethodCall::send() const
MethodReply MethodCall::send(uint64_t timeout) const
{
if (!doesntExpectReply())
return sendWithReply();
return sendWithReply(timeout);
else
return sendWithNoReply();
}
MethodReply MethodCall::sendWithReply() const
MethodReply MethodCall::sendWithReply(uint64_t timeout) const
{
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
sd_bus_message* sdbusReply{};
auto r = sdbus_->sd_bus_call(nullptr, (sd_bus_message*)msg_, 0, &sdbusError, &sdbusReply);
auto r = sdbus_->sd_bus_call(nullptr, (sd_bus_message*)msg_, timeout, &sdbusError, &sdbusReply);
if (sd_bus_error_is_set(&sdbusError))
throw sdbus::Error(sdbusError.name, sdbusError.message);
@@ -682,11 +687,11 @@ AsyncMethodCall::AsyncMethodCall(MethodCall&& call) noexcept
{
}
AsyncMethodCall::Slot AsyncMethodCall::send(void* callback, void* userData) const
AsyncMethodCall::Slot AsyncMethodCall::send(void* callback, void* userData, uint64_t timeout) const
{
sd_bus_slot* slot;
auto r = sdbus_->sd_bus_call_async(nullptr, &slot, (sd_bus_message*)msg_, (sd_bus_message_handler_t)callback, userData, 0);
auto r = sdbus_->sd_bus_call_async(nullptr, &slot, (sd_bus_message*)msg_, (sd_bus_message_handler_t)callback, userData, timeout);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method asynchronously", -r);
return Slot{slot, [sdbus_ = sdbus_](void *slot){ sdbus_->sd_bus_slot_unref((sd_bus_slot*)slot); }};
+4 -4
View File
@@ -69,17 +69,17 @@ AsyncMethodCall Proxy::createAsyncMethodCall(const std::string& interfaceName, c
return AsyncMethodCall{Proxy::createMethodCall(interfaceName, methodName)};
}
MethodReply Proxy::callMethod(const MethodCall& message)
MethodReply Proxy::callMethod(const MethodCall& message, uint64_t timeout)
{
return message.send();
return message.send(timeout);
}
void Proxy::callMethod(const AsyncMethodCall& message, async_reply_handler asyncReplyCallback)
void Proxy::callMethod(const AsyncMethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout)
{
auto callback = (void*)&Proxy::sdbus_async_reply_handler;
auto callData = std::make_unique<AsyncCalls::CallData>(AsyncCalls::CallData{*this, std::move(asyncReplyCallback), {}});
callData->slot = message.send(callback, callData.get());
callData->slot = message.send(callback, callData.get(), timeout);
pendingAsyncCalls_.addCall(callData->slot.get(), std::move(callData));
}
+2 -2
View File
@@ -52,8 +52,8 @@ namespace internal {
MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
AsyncMethodCall createAsyncMethodCall(const std::string& interfaceName, const std::string& methodName) override;
MethodReply callMethod(const MethodCall& message) override;
void callMethod(const AsyncMethodCall& message, async_reply_handler asyncReplyCallback) override;
MethodReply callMethod(const MethodCall& message, uint64_t timeout) override;
void callMethod(const AsyncMethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout) override;
void registerSignalHandler( const std::string& interfaceName
, const std::string& signalName
+32
View File
@@ -26,6 +26,7 @@
*/
#include "SdBus.h"
#include <sdbus-c++/Error.h>
namespace sdbus { namespace internal {
@@ -92,6 +93,32 @@ int SdBus::sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message
return ::sd_bus_message_new_method_error(call, m, e);
}
int SdBus::sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec)
{
#if LIBSYSTEMD_VERSION>=240
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
return ::sd_bus_set_method_call_timeout(bus, usec);
#else
(void)bus;
(void)usec;
throw sdbus::Error(SD_BUS_ERROR_NOT_SUPPORTED, "Setting general method call timeout not supported by underlying version of libsystemd");
#endif
}
int SdBus::sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret)
{
#if LIBSYSTEMD_VERSION>=240
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
return ::sd_bus_get_method_call_timeout(bus, ret);
#else
(void)bus;
(void)ret;
throw sdbus::Error(SD_BUS_ERROR_NOT_SUPPORTED, "Getting general method call timeout not supported by underlying version of libsystemd");
#endif
}
int SdBus::sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
@@ -137,6 +164,11 @@ int SdBus::sd_bus_open_system(sd_bus **ret)
return ::sd_bus_open_system(ret);
}
int SdBus::sd_bus_open_system_remote(sd_bus **ret, const char *host)
{
return ::sd_bus_open_system_remote(ret, host);
}
int SdBus::sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
+4
View File
@@ -48,6 +48,9 @@ public:
virtual int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m) override;
virtual int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e) override;
virtual int sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec) override;
virtual int sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret) override;
virtual int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names) override;
virtual int sd_bus_emit_object_added(sd_bus *bus, const char *path) override;
virtual int sd_bus_emit_object_removed(sd_bus *bus, const char *path) override;
@@ -56,6 +59,7 @@ public:
virtual int sd_bus_open_user(sd_bus **ret) override;
virtual int sd_bus_open_system(sd_bus **ret) override;
virtual int sd_bus_open_system_remote(sd_bus **ret, const char* hsot) 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_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) override;
+2
View File
@@ -85,12 +85,14 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
#----------------------------------
add_executable(sdbus-c++-unit-tests ${UNITTESTS_SRCS} $<TARGET_OBJECTS:sdbus-c++-objlib>)
target_compile_definitions(sdbus-c++-unit-tests PRIVATE LIBSYSTEMD_VERSION=${SYSTEMD_VERSION})
target_include_directories(sdbus-c++-unit-tests PRIVATE ${SYSTEMD_INCLUDE_DIRS}
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/include)
target_link_libraries(sdbus-c++-unit-tests ${SYSTEMD_LIBRARIES} gmock gmock_main)
add_executable(sdbus-c++-integration-tests ${INTEGRATIONTESTS_SRCS})
target_compile_definitions(sdbus-c++-integration-tests PRIVATE LIBSYSTEMD_VERSION=${SYSTEMD_VERSION})
target_link_libraries(sdbus-c++-integration-tests sdbus-c++ gmock gmock_main)
# Manual performance and stress tests
@@ -43,6 +43,8 @@
#include <fstream>
#include <future>
#include <unistd.h>
using ::testing::Eq;
using ::testing::DoubleEq;
using ::testing::Gt;
@@ -230,6 +232,30 @@ TEST_F(SdbusTestObject, CallsMultiplyMethodWithNoReplyFlag)
ASSERT_THAT(m_adaptor->m_multiplyResult, Eq(INT64_VALUE * DOUBLE_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithCustomTimeoutSuccessfully)
{
auto res = m_proxy->doOperationWith500msTimeout(20); // The operation will take 20ms, but the timeout is 500ms, so we are fine
ASSERT_THAT(res, Eq(20));
}
TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenMethodTimesOut)
{
try
{
m_proxy->doOperationWith500msTimeout(1000); // The operation will take 1s, but the timeout is 500ms, so we should time out
FAIL() << "Expected sdbus::Error exception";
}
catch (const sdbus::Error& e)
{
ASSERT_THAT(e.getName(), Eq("org.freedesktop.DBus.Error.Timeout"));
ASSERT_THAT(e.getMessage(), Eq("Connection timed out"));
}
catch(...)
{
FAIL() << "Expected sdbus::Error exception";
}
}
TEST_F(SdbusTestObject, CallsMethodThatThrowsError)
{
try
@@ -366,6 +392,20 @@ TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentObject)
ASSERT_THROW(proxy.getInt(), sdbus::Error);
}
#if LIBSYSTEMD_VERSION>=240
TEST_F(SdbusTestObject, CanSetGeneralMethodTimeoutWithLibsystemdVersionGreaterThan239)
{
s_connection->setMethodCallTimeout(5000000);
ASSERT_THAT(s_connection->getMethodCallTimeout(), Eq(5000000));
}
#else
TEST_F(SdbusTestObject, CannotSetGeneralMethodTimeoutWithLibsystemdVersionLessThan240)
{
ASSERT_THROW(s_connection->setMethodCallTimeout(5000000), sdbus::Error);
ASSERT_THROW(s_connection->getMethodCallTimeout(), sdbus::Error);
}
#endif
// Signals
TEST_F(SdbusTestObject, EmitsSimpleSignalSuccesfully)
@@ -431,6 +471,13 @@ TEST_F(SdbusTestObject, PingsViaPeerInterface)
TEST_F(SdbusTestObject, AnswersMachineUuidViaPeerInterface)
{
// If /etc/machine-id does not exist in your system (which is very likely because you have
// a non-systemd Linux), org.freedesktop.DBus.Peer.GetMachineId() will not work. To solve
// this, you can create /etc/machine-id yourself as symlink to /var/lib/dbus/machine-id,
// and then org.freedesktop.DBus.Peer.GetMachineId() will start to work.
if (::access("/etc/machine-id", F_OK) == -1)
GTEST_SKIP() << "/etc/machine-id file does not exist, GetMachineId() will not work";
ASSERT_NO_THROW(m_proxy->GetMachineId());
}
-2
View File
@@ -122,6 +122,4 @@ public: // for tests
std::function<void(const sdbus::ObjectPath&, const std::vector<std::string>&)> m_onInterfacesRemovedHandler;
};
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTINGPROXY_H_ */
+8
View File
@@ -136,6 +136,14 @@ public:
return result;
}
uint32_t doOperationWith500msTimeout(uint32_t param)
{
using namespace std::chrono_literals;
uint32_t result;
object_.callMethod("doOperation").onInterface(INTERFACE_NAME).withTimeout(500000us).withArguments(param).storeResultsTo(result);
return result;
}
uint32_t doOperationAsync(uint32_t param)
{
uint32_t result;
+78 -57
View File
@@ -35,17 +35,18 @@ using ::testing::DoAll;
using ::testing::SetArgPointee;
using ::testing::Return;
using ::testing::NiceMock;
using BusType = sdbus::internal::Connection::BusType;
using sdbus::internal::Connection;
constexpr sdbus::internal::Connection::system_bus_t system_bus;
constexpr sdbus::internal::Connection::session_bus_t session_bus;
constexpr sdbus::internal::Connection::remote_system_bus_t remote_system_bus;
class ConnectionCreationTest : public ::testing::Test
{
protected:
ConnectionCreationTest() = default;
std::unique_ptr<NiceMock<SdBusMock>> mock_ { std::make_unique<NiceMock<SdBusMock>>() };
sd_bus* STUB_ { reinterpret_cast<sd_bus*>(1) };
std::unique_ptr<NiceMock<SdBusMock>> sdBusIntfMock_ = std::make_unique<NiceMock<SdBusMock>>();
sd_bus* fakeBusPtr_ = reinterpret_cast<sd_bus*>(1);
};
using ASystemBusConnection = ConnectionCreationTest;
@@ -53,99 +54,119 @@ using ASessionBusConnection = ConnectionCreationTest;
TEST_F(ASystemBusConnection, OpensAndFlushesBusWhenCreated)
{
EXPECT_CALL(*mock_, sd_bus_open_system(_)).WillOnce(DoAll(SetArgPointee<0>(STUB_), Return(1)));
EXPECT_CALL(*mock_, sd_bus_flush(_)).Times(1);
sdbus::internal::Connection(BusType::eSystem, std::move(mock_));
EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_system(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
EXPECT_CALL(*sdBusIntfMock_, sd_bus_flush(_)).Times(1);
Connection(std::move(sdBusIntfMock_), system_bus);
}
TEST_F(ASessionBusConnection, OpensAndFlushesBusWhenCreated)
{
EXPECT_CALL(*mock_, sd_bus_open_user(_)).WillOnce(DoAll(SetArgPointee<0>(STUB_), Return(1)));
EXPECT_CALL(*mock_, sd_bus_flush(_)).Times(1);
sdbus::internal::Connection(BusType::eSession, std::move(mock_));
EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
EXPECT_CALL(*sdBusIntfMock_, sd_bus_flush(_)).Times(1);
Connection(std::move(sdBusIntfMock_), session_bus);
}
TEST_F(ASystemBusConnection, ClosesAndUnrefsBusWhenDestructed)
{
ON_CALL(*mock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(STUB_), Return(1)));
EXPECT_CALL(*mock_, sd_bus_flush_close_unref(_)).Times(1);
sdbus::internal::Connection(BusType::eSession, std::move(mock_));
ON_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
EXPECT_CALL(*sdBusIntfMock_, sd_bus_flush_close_unref(_)).Times(1);
Connection(std::move(sdBusIntfMock_), session_bus);
}
TEST_F(ASessionBusConnection, ClosesAndUnrefsBusWhenDestructed)
{
ON_CALL(*mock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(STUB_), Return(1)));
EXPECT_CALL(*mock_, sd_bus_flush_close_unref(_)).Times(1);
sdbus::internal::Connection(BusType::eSession, std::move(mock_));
ON_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
EXPECT_CALL(*sdBusIntfMock_, sd_bus_flush_close_unref(_)).Times(1);
Connection(std::move(sdBusIntfMock_), session_bus);
}
TEST_F(ASystemBusConnection, ThrowsErrorWhenOpeningTheBusFailsDuringConstruction)
{
ON_CALL(*mock_, sd_bus_open_system(_)).WillByDefault(DoAll(SetArgPointee<0>(STUB_), Return(-1)));
ASSERT_THROW(sdbus::internal::Connection(BusType::eSystem, std::move(mock_)), sdbus::Error);
ON_CALL(*sdBusIntfMock_, sd_bus_open_system(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(-1)));
ASSERT_THROW(Connection(std::move(sdBusIntfMock_), system_bus), sdbus::Error);
}
TEST_F(ASessionBusConnection, ThrowsErrorWhenOpeningTheBusFailsDuringConstruction)
{
ON_CALL(*mock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(STUB_), Return(-1)));
ASSERT_THROW(sdbus::internal::Connection(BusType::eSession, std::move(mock_)), sdbus::Error);
ON_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(-1)));
ASSERT_THROW(Connection(std::move(sdBusIntfMock_), session_bus), sdbus::Error);
}
TEST_F(ASystemBusConnection, ThrowsErrorWhenFlushingTheBusFailsDuringConstruction)
{
ON_CALL(*mock_, sd_bus_open_system(_)).WillByDefault(DoAll(SetArgPointee<0>(STUB_), Return(1)));
ON_CALL(*mock_, sd_bus_flush(_)).WillByDefault(Return(-1));
ASSERT_THROW(sdbus::internal::Connection(BusType::eSystem, std::move(mock_)), sdbus::Error);
ON_CALL(*sdBusIntfMock_, sd_bus_open_system(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
ON_CALL(*sdBusIntfMock_, sd_bus_flush(_)).WillByDefault(Return(-1));
ASSERT_THROW(Connection(std::move(sdBusIntfMock_), system_bus), sdbus::Error);
}
TEST_F(ASessionBusConnection, ThrowsErrorWhenFlushingTheBusFailsDuringConstruction)
{
ON_CALL(*mock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(STUB_), Return(1)));
ON_CALL(*mock_, sd_bus_flush(_)).WillByDefault(Return(-1));
ASSERT_THROW(sdbus::internal::Connection(BusType::eSession, std::move(mock_)), sdbus::Error);
ON_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
ON_CALL(*sdBusIntfMock_, sd_bus_flush(_)).WillByDefault(Return(-1));
ASSERT_THROW(Connection(std::move(sdBusIntfMock_), session_bus), sdbus::Error);
}
class ConnectionRequestTest : public ::testing::TestWithParam<BusType>
namespace
{
template <typename _BusTypeTag>
class AConnectionNameRequest : public ::testing::Test
{
protected:
ConnectionRequestTest() = default;
void setUpBusOpenExpectation();
std::unique_ptr<Connection> makeConnection();
void SetUp() override
{
switch (GetParam())
{
case BusType::eSystem:
EXPECT_CALL(*mock_, sd_bus_open_system(_)).WillOnce(DoAll(SetArgPointee<0>(STUB_), Return(1)));
break;
case BusType::eSession:
EXPECT_CALL(*mock_, sd_bus_open_user(_)).WillOnce(DoAll(SetArgPointee<0>(STUB_), Return(1)));
break;
default:
break;
}
ON_CALL(*mock_, sd_bus_flush(_)).WillByDefault(Return(1));
ON_CALL(*mock_, sd_bus_flush_close_unref(_)).WillByDefault(Return(STUB_));
setUpBusOpenExpectation();
ON_CALL(*sdBusIntfMock_, sd_bus_flush(_)).WillByDefault(Return(1));
ON_CALL(*sdBusIntfMock_, sd_bus_flush_close_unref(_)).WillByDefault(Return(fakeBusPtr_));
con_ = makeConnection();
}
std::unique_ptr<NiceMock<SdBusMock>> mock_ { std::make_unique<NiceMock<SdBusMock>>() };
sd_bus* STUB_ { reinterpret_cast<sd_bus*>(1) };
NiceMock<SdBusMock>* sdBusIntfMock_ = new NiceMock<SdBusMock>(); // con_ below will assume ownership
sd_bus* fakeBusPtr_ = reinterpret_cast<sd_bus*>(1);
std::unique_ptr<Connection> con_;
};
using AConnectionNameRequest = ConnectionRequestTest;
TEST_P(AConnectionNameRequest, DoesNotThrowOnSuccess)
template<> void AConnectionNameRequest<Connection::system_bus_t>::setUpBusOpenExpectation()
{
EXPECT_CALL(*mock_, sd_bus_request_name(_, _, _)).WillOnce(Return(1));
sdbus::internal::Connection(GetParam(), std::move(mock_)).requestName("");
EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_system(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
}
template<> void AConnectionNameRequest<Connection::session_bus_t>::setUpBusOpenExpectation()
{
EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
}
template<> void AConnectionNameRequest<Connection::remote_system_bus_t>::setUpBusOpenExpectation()
{
EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_system_remote(_, _)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
}
template <typename _BusTypeTag>
std::unique_ptr<Connection> AConnectionNameRequest<_BusTypeTag>::makeConnection()
{
return std::make_unique<Connection>(std::unique_ptr<NiceMock<SdBusMock>>(sdBusIntfMock_), _BusTypeTag{});
}
template<> std::unique_ptr<Connection> AConnectionNameRequest<Connection::remote_system_bus_t>::makeConnection()
{
return std::make_unique<Connection>(std::unique_ptr<NiceMock<SdBusMock>>(sdBusIntfMock_), remote_system_bus, "some host");
}
TEST_P(AConnectionNameRequest, ThrowsOnFail)
{
EXPECT_CALL(*mock_, sd_bus_request_name(_, _, _)).WillOnce(Return(-1));
typedef ::testing::Types< Connection::system_bus_t
, Connection::session_bus_t
, Connection::remote_system_bus_t
> BusTypeTags;
sdbus::internal::Connection conn_(GetParam(), std::move(mock_));
ASSERT_THROW(conn_.requestName(""), sdbus::Error);
TYPED_TEST_SUITE(AConnectionNameRequest, BusTypeTags);
}
// INSTANTIATE_TEST_SUITE_P is defined in googletest master, but not in googletest v1.8.1 that we are using now
INSTANTIATE_TEST_CASE_P(Request, AConnectionNameRequest, ::testing::Values(BusType::eSystem, BusType::eSession));
//INSTANTIATE_TEST_SUITE_P(Request, AConnectionNameRequest, ::testing::Values(BusType::eSystem, BusType::eSession))
TYPED_TEST(AConnectionNameRequest, DoesNotThrowOnSuccess)
{
EXPECT_CALL(*this->sdBusIntfMock_, sd_bus_request_name(_, _, _)).WillOnce(Return(1));
this->con_->requestName("org.sdbuscpp.somename");
}
TYPED_TEST(AConnectionNameRequest, ThrowsOnFail)
{
EXPECT_CALL(*this->sdBusIntfMock_, sd_bus_request_name(_, _, _)).WillOnce(Return(-1));
ASSERT_THROW(this->con_->requestName("org.sdbuscpp.somename"), sdbus::Error);
}
+1 -1
View File
@@ -121,7 +121,7 @@ namespace
, ComplexType
> DBusSupportedTypes;
TYPED_TEST_CASE(Type2DBusTypeSignatureConversion, DBusSupportedTypes);
TYPED_TEST_SUITE(Type2DBusTypeSignatureConversion, DBusSupportedTypes);
}
/*-------------------------------------*/
+4
View File
@@ -47,6 +47,9 @@ public:
MOCK_METHOD2(sd_bus_message_new_method_return, int(sd_bus_message *call, sd_bus_message **m));
MOCK_METHOD3(sd_bus_message_new_method_error, int(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e));
MOCK_METHOD2(sd_bus_set_method_call_timeout, int(sd_bus *bus, uint64_t usec));
MOCK_METHOD2(sd_bus_get_method_call_timeout, int(sd_bus *bus, uint64_t *ret));
MOCK_METHOD4(sd_bus_emit_properties_changed_strv, int(sd_bus *bus, const char *path, const char *interface, char **names));
MOCK_METHOD2(sd_bus_emit_object_added, int(sd_bus *bus, const char *path));
MOCK_METHOD2(sd_bus_emit_object_removed, int(sd_bus *bus, const char *path));
@@ -55,6 +58,7 @@ public:
MOCK_METHOD1(sd_bus_open_user, int(sd_bus **ret));
MOCK_METHOD1(sd_bus_open_system, int(sd_bus **ret));
MOCK_METHOD2(sd_bus_open_system_remote, int(sd_bus **ret, const char *host));
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_METHOD6(sd_bus_add_object_vtable, int(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata));
+1 -1
View File
@@ -193,7 +193,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
if (annotationValue == "true")
annotationRegistration += ".markAsPrivileged()";
}
else
else if (annotationName != "org.freedesktop.DBus.Method.Timeout") // Whatever else...
{
std::cerr << "Node: " << methodName << ": "
<< "Option '" << annotationName << "' not allowed or supported in this context! Option ignored..." << std::endl;
+20 -1
View File
@@ -34,7 +34,6 @@
#include <algorithm>
#include <iterator>
using std::endl;
using sdbuscpp::xml::Document;
@@ -142,6 +141,8 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
bool dontExpectReply{false};
bool async{false};
std::string timeoutValue;
Nodes annotations = (*method)["annotation"];
for (const auto& annotation : annotations)
{
@@ -150,6 +151,8 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
else if (annotation->get("name") == "org.freedesktop.DBus.Method.Async"
&& (annotation->get("value") == "client" || annotation->get("value") == "clientserver"))
async = true;
if (annotation->get("name") == "org.freedesktop.DBus.Method.Timeout")
timeoutValue = annotation->get("value");
}
if (dontExpectReply && outArgs.size() > 0)
{
@@ -157,6 +160,12 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
std::cerr << "Option 'org.freedesktop.DBus.Method.NoReply' not allowed for methods with 'out' variables! Option ignored..." << std::endl;
dontExpectReply = false;
}
if (!timeoutValue.empty() && dontExpectReply)
{
std::cerr << "Function: " << name << ": ";
std::cerr << "Option 'org.freedesktop.DBus.Method.Timeout' not allowed for 'NoReply' methods! Option ignored..." << std::endl;
timeoutValue.clear();
}
auto retType = outArgsToType(outArgs);
std::string inArgStr, inArgTypeStr;
@@ -167,6 +176,11 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
definitionSS << tab << (async ? "void" : retType) << " " << name << "(" << inArgTypeStr << ")" << endl
<< tab << "{" << endl;
if (!timeoutValue.empty())
{
definitionSS << tab << tab << "using namespace std::chrono_literals;" << endl;
}
if (outArgs.size() > 0 && !async)
{
definitionSS << tab << tab << retType << " result;" << endl;
@@ -175,6 +189,11 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
definitionSS << tab << tab << "proxy_.callMethod" << (async ? "Async" : "") << "(\"" << name << "\")"
".onInterface(INTERFACE_NAME)";
if (!timeoutValue.empty())
{
definitionSS << ".withTimeout(" << timeoutValue << "us)";
}
if (inArgs.size() > 0)
{
definitionSS << ".withArguments(" << inArgStr << ")";