mirror of
https://github.com/Kistler-Group/sdbus-cpp.git
synced 2026-08-28 16:02:37 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc495811f9 | ||
|
|
839bc13625 | ||
|
|
5fe0f503ca | ||
|
|
d50a15b2a2 | ||
|
|
3a76e9c120 | ||
|
|
304b69dd8b | ||
|
|
099bc857ad | ||
|
|
c139110112 | ||
|
|
e7155c5506 | ||
|
|
0f7de608ac | ||
|
|
c6d4d2710f | ||
|
|
0440dcb15b | ||
|
|
e30ce194ab | ||
|
|
8dea11bac6 |
+2
-3
@@ -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.5 LANGUAGES C CXX)
|
||||
|
||||
include(GNUInstallDirs) # Installation directories for `install` command and pkgconfig file
|
||||
|
||||
@@ -37,7 +37,6 @@ set(SDBUSCPP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include/${SDBUSCPP_INCLUDE_
|
||||
|
||||
set(SDBUSCPP_CPP_SRCS
|
||||
${SDBUSCPP_SOURCE_DIR}/Connection.cpp
|
||||
${SDBUSCPP_SOURCE_DIR}/ConvenienceApiClasses.cpp
|
||||
${SDBUSCPP_SOURCE_DIR}/Error.cpp
|
||||
${SDBUSCPP_SOURCE_DIR}/Message.cpp
|
||||
${SDBUSCPP_SOURCE_DIR}/Object.cpp
|
||||
@@ -98,7 +97,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}>)
|
||||
|
||||
@@ -114,3 +114,17 @@ 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
|
||||
|
||||
v0.7.5
|
||||
- [[Breaking ABI change]] No more hiding from C++17: Move API code containing C++17 uncaught_exceptions calls from within library to public API
|
||||
- Add a method to retrieve the unique name of a connection
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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
|
||||
---------------------------
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#include <sdbus-c++/Flags.h>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
@@ -51,7 +53,7 @@ namespace sdbus {
|
||||
MethodRegistrator& operator=(MethodRegistrator&& other) = default;
|
||||
~MethodRegistrator() noexcept(false);
|
||||
|
||||
MethodRegistrator& onInterface(const std::string& interfaceName);
|
||||
MethodRegistrator& onInterface(std::string interfaceName);
|
||||
template <typename _Function>
|
||||
std::enable_if_t<!is_async_method_v<_Function>, MethodRegistrator&> implementedAs(_Function&& callback);
|
||||
template <typename _Function>
|
||||
@@ -100,7 +102,7 @@ namespace sdbus {
|
||||
PropertyRegistrator& operator=(PropertyRegistrator&& other) = default;
|
||||
~PropertyRegistrator() noexcept(false);
|
||||
|
||||
PropertyRegistrator& onInterface(const std::string& interfaceName);
|
||||
PropertyRegistrator& onInterface(std::string interfaceName);
|
||||
template <typename _Function> PropertyRegistrator& withGetter(_Function&& callback);
|
||||
template <typename _Function> PropertyRegistrator& withSetter(_Function&& callback);
|
||||
PropertyRegistrator& markAsDeprecated();
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
@@ -195,12 +205,12 @@ namespace sdbus {
|
||||
{
|
||||
public:
|
||||
SignalSubscriber(IProxy& proxy, const std::string& signalName);
|
||||
SignalSubscriber& onInterface(const std::string& interfaceName);
|
||||
SignalSubscriber& onInterface(std::string interfaceName);
|
||||
template <typename _Function> void call(_Function&& callback);
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
std::string signalName_;
|
||||
const std::string& signalName_;
|
||||
std::string interfaceName_;
|
||||
};
|
||||
|
||||
@@ -212,14 +222,14 @@ namespace sdbus {
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
std::string propertyName_;
|
||||
const std::string& propertyName_;
|
||||
};
|
||||
|
||||
class PropertySetter
|
||||
{
|
||||
public:
|
||||
PropertySetter(IProxy& proxy, const std::string& propertyName);
|
||||
PropertySetter& onInterface(const std::string& interfaceName);
|
||||
PropertySetter& onInterface(std::string interfaceName);
|
||||
template <typename _Value> void toValue(const _Value& value);
|
||||
void toValue(const sdbus::Variant& value);
|
||||
|
||||
|
||||
@@ -36,16 +36,19 @@
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
/*#include <exception>*/
|
||||
#include <exception>
|
||||
#include <cassert>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
// Moved into the library to isolate from C++17 dependency
|
||||
/*
|
||||
/*** ----------------- ***/
|
||||
/*** MethodRegistrator ***/
|
||||
/*** ----------------- ***/
|
||||
|
||||
inline MethodRegistrator::MethodRegistrator(IObject& object, const std::string& methodName)
|
||||
: object_(object)
|
||||
, methodName_(methodName)
|
||||
, exceptions_(std::uncaught_exceptions()) // Needs C++17
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -55,8 +58,8 @@ namespace sdbus {
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus method", EINVAL);
|
||||
SDBUS_THROW_ERROR_IF(!methodCallback_, "Method handler not specified when registering a DBus method", EINVAL);
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
assert(methodCallback_); // implementedAs() must be placed/called prior to this function
|
||||
|
||||
// registerMethod() can throw. But as the MethodRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
@@ -69,11 +72,10 @@ namespace sdbus {
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.registerMethod(interfaceName_, methodName_, inputSignature_, outputSignature_, std::move(methodCallback_), flags_);
|
||||
}
|
||||
*/
|
||||
|
||||
inline MethodRegistrator& MethodRegistrator::onInterface(const std::string& interfaceName)
|
||||
inline MethodRegistrator& MethodRegistrator::onInterface(std::string interfaceName)
|
||||
{
|
||||
interfaceName_ = interfaceName;
|
||||
interfaceName_ = std::move(interfaceName);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -149,12 +151,13 @@ namespace sdbus {
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*** ----------------- ***/
|
||||
/*** SignalRegistrator ***/
|
||||
/*** ----------------- ***/
|
||||
|
||||
// Moved into the library to isolate from C++17 dependency
|
||||
/*
|
||||
inline SignalRegistrator::SignalRegistrator(IObject& object, std::string signalName)
|
||||
inline SignalRegistrator::SignalRegistrator(IObject& object, const std::string& signalName)
|
||||
: object_(object)
|
||||
, signalName_(std::move(signalName))
|
||||
, signalName_(signalName)
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
@@ -165,8 +168,7 @@ namespace sdbus {
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
if (interfaceName_.empty())
|
||||
throw sdbus::Exception("DBus interface not specified when registering a DBus signal");
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
// registerSignal() can throw. But as the SignalRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
@@ -177,9 +179,8 @@ namespace sdbus {
|
||||
// Therefore, we can allow registerSignal() 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.
|
||||
object_.registerSignal(interfaceName_, signalName_, signalSignature_);
|
||||
object_.registerSignal(interfaceName_, signalName_, signalSignature_, flags_);
|
||||
}
|
||||
*/
|
||||
|
||||
inline SignalRegistrator& SignalRegistrator::onInterface(std::string interfaceName)
|
||||
{
|
||||
@@ -203,12 +204,13 @@ namespace sdbus {
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*** ------------------- ***/
|
||||
/*** PropertyRegistrator ***/
|
||||
/*** ------------------- ***/
|
||||
|
||||
// Moved into the library to isolate from C++17 dependency
|
||||
/*
|
||||
inline PropertyRegistrator::PropertyRegistrator(IObject& object, std::string propertyName)
|
||||
inline PropertyRegistrator::PropertyRegistrator(IObject& object, const std::string& propertyName)
|
||||
: object_(object)
|
||||
, propertyName_(std::move(propertyName))
|
||||
, propertyName_(propertyName)
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
@@ -219,7 +221,7 @@ namespace sdbus {
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus property", EINVAL);
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
// registerProperty() can throw. But as the PropertyRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
@@ -230,17 +232,17 @@ namespace sdbus {
|
||||
// Therefore, we can allow registerProperty() 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.
|
||||
object_.registerProperty( std::move(interfaceName_)
|
||||
, std::move(propertyName_)
|
||||
, std::move(propertySignature_)
|
||||
object_.registerProperty( interfaceName_
|
||||
, propertyName_
|
||||
, propertySignature_
|
||||
, std::move(getter_)
|
||||
, std::move(setter_) );
|
||||
, std::move(setter_)
|
||||
, flags_ );
|
||||
}
|
||||
*/
|
||||
|
||||
inline PropertyRegistrator& PropertyRegistrator::onInterface(const std::string& interfaceName)
|
||||
inline PropertyRegistrator& PropertyRegistrator::onInterface(std::string interfaceName)
|
||||
{
|
||||
interfaceName_ = interfaceName;
|
||||
interfaceName_ = std::move(interfaceName);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -309,9 +311,10 @@ namespace sdbus {
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*** -------------------- ***/
|
||||
/*** InterfaceFlagsSetter ***/
|
||||
/*** -------------------- ***/
|
||||
|
||||
// Moved into the library to isolate from C++17 dependency
|
||||
/*
|
||||
inline InterfaceFlagsSetter::InterfaceFlagsSetter(IObject& object, const std::string& interfaceName)
|
||||
: object_(object)
|
||||
, interfaceName_(interfaceName)
|
||||
@@ -325,8 +328,6 @@ namespace sdbus {
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when setting its flags", EINVAL);
|
||||
|
||||
// setInterfaceFlags() can throw. But as the InterfaceFlagsSetter shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
@@ -336,10 +337,8 @@ namespace sdbus {
|
||||
// Therefore, we can allow setInterfaceFlags() 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.
|
||||
object_.setInterfaceFlags( std::move(interfaceName_)
|
||||
, std::move(flags_) );
|
||||
object_.setInterfaceFlags(interfaceName_, std::move(flags_));
|
||||
}
|
||||
*/
|
||||
|
||||
inline InterfaceFlagsSetter& InterfaceFlagsSetter::markAsDeprecated()
|
||||
{
|
||||
@@ -369,9 +368,10 @@ namespace sdbus {
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*** ------------- ***/
|
||||
/*** SignalEmitter ***/
|
||||
/*** ------------- ***/
|
||||
|
||||
// Moved into the library to isolate from C++17 dependency
|
||||
/*
|
||||
inline SignalEmitter::SignalEmitter(IObject& object, const std::string& signalName)
|
||||
: object_(object)
|
||||
, signalName_(signalName)
|
||||
@@ -385,9 +385,6 @@ namespace sdbus {
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
if (!signal_.isValid())
|
||||
throw sdbus::Exception("DBus interface not specified when emitting a DBus signal");
|
||||
|
||||
// emitSignal() can throw. But as the SignalEmitter shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
@@ -399,7 +396,6 @@ namespace sdbus {
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.emitSignal(signal_);
|
||||
}
|
||||
*/
|
||||
|
||||
inline SignalEmitter& SignalEmitter::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
@@ -411,15 +407,16 @@ namespace sdbus {
|
||||
template <typename... _Args>
|
||||
inline void SignalEmitter::withArguments(_Args&&... args)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!signal_.isValid(), "DBus interface not specified when emitting a DBus signal", EINVAL);
|
||||
assert(signal_.isValid()); // onInterface() must be placed/called prior to withArguments()
|
||||
|
||||
detail::serialize_pack(signal_, std::forward<_Args>(args)...);
|
||||
}
|
||||
|
||||
/*** ------------- ***/
|
||||
/*** MethodInvoker ***/
|
||||
/*** ------------- ***/
|
||||
|
||||
// Moved into the library to isolate from C++17 dependency
|
||||
/*
|
||||
inline MethodInvoker::MethodInvoker(IProxy& proxyObject, const std::string& methodName)
|
||||
inline MethodInvoker::MethodInvoker(IProxy& proxy, const std::string& methodName)
|
||||
: proxy_(proxy)
|
||||
, methodName_(methodName)
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
@@ -433,9 +430,6 @@ namespace sdbus {
|
||||
if (methodCalled_ || std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
if (!method_.isValid())
|
||||
throw sdbus::Exception("DBus interface not specified when calling a DBus method");
|
||||
|
||||
// callMethod() can throw. But as the MethodInvoker shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
@@ -445,9 +439,8 @@ namespace sdbus {
|
||||
// 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_);
|
||||
}
|
||||
*/
|
||||
|
||||
inline MethodInvoker& MethodInvoker::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
@@ -456,10 +449,24 @@ 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)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!method_.isValid(), "DBus interface not specified when calling a DBus method", EINVAL);
|
||||
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
detail::serialize_pack(method_, std::forward<_Args>(args)...);
|
||||
|
||||
@@ -469,9 +476,9 @@ namespace sdbus {
|
||||
template <typename... _Args>
|
||||
inline void MethodInvoker::storeResultsTo(_Args&... args)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!method_.isValid(), "DBus interface not specified when calling a DBus method", EINVAL);
|
||||
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
auto reply = proxy_.callMethod(method_);
|
||||
auto reply = proxy_.callMethod(method_, timeout_);
|
||||
methodCalled_ = true;
|
||||
|
||||
detail::deserialize_pack(reply, args...);
|
||||
@@ -479,11 +486,14 @@ namespace sdbus {
|
||||
|
||||
inline void MethodInvoker::dontExpectReply()
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!method_.isValid(), "DBus interface not specified when calling a DBus method", EINVAL);
|
||||
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
method_.dontExpectReply();
|
||||
}
|
||||
|
||||
/*** ------------------ ***/
|
||||
/*** AsyncMethodInvoker ***/
|
||||
/*** ------------------ ***/
|
||||
|
||||
inline AsyncMethodInvoker::AsyncMethodInvoker(IProxy& proxy, const std::string& methodName)
|
||||
: proxy_(proxy)
|
||||
@@ -498,10 +508,24 @@ 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)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!method_.isValid(), "DBus interface not specified when calling a DBus method", EINVAL);
|
||||
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
detail::serialize_pack(method_, std::forward<_Args>(args)...);
|
||||
|
||||
@@ -511,9 +535,9 @@ namespace sdbus {
|
||||
template <typename _Function>
|
||||
void AsyncMethodInvoker::uponReplyInvoke(_Function&& callback)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!method_.isValid(), "DBus interface not specified when calling a DBus method", EINVAL);
|
||||
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
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 +549,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)
|
||||
@@ -535,9 +564,9 @@ namespace sdbus {
|
||||
{
|
||||
}
|
||||
|
||||
inline SignalSubscriber& SignalSubscriber::onInterface(const std::string& interfaceName)
|
||||
inline SignalSubscriber& SignalSubscriber::onInterface(std::string interfaceName)
|
||||
{
|
||||
interfaceName_ = interfaceName;
|
||||
interfaceName_ = std::move(interfaceName);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -545,7 +574,7 @@ namespace sdbus {
|
||||
template <typename _Function>
|
||||
inline void SignalSubscriber::call(_Function&& callback)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when subscribing to a signal", EINVAL);
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
proxy_.registerSignalHandler( interfaceName_
|
||||
, signalName_
|
||||
@@ -563,6 +592,9 @@ namespace sdbus {
|
||||
});
|
||||
}
|
||||
|
||||
/*** -------------- ***/
|
||||
/*** PropertyGetter ***/
|
||||
/*** -------------- ***/
|
||||
|
||||
inline PropertyGetter::PropertyGetter(IProxy& proxy, const std::string& propertyName)
|
||||
: proxy_(proxy)
|
||||
@@ -581,6 +613,9 @@ namespace sdbus {
|
||||
return var;
|
||||
}
|
||||
|
||||
/*** -------------- ***/
|
||||
/*** PropertySetter ***/
|
||||
/*** -------------- ***/
|
||||
|
||||
inline PropertySetter::PropertySetter(IProxy& proxy, const std::string& propertyName)
|
||||
: proxy_(proxy)
|
||||
@@ -588,9 +623,9 @@ namespace sdbus {
|
||||
{
|
||||
}
|
||||
|
||||
inline PropertySetter& PropertySetter::onInterface(const std::string& interfaceName)
|
||||
inline PropertySetter& PropertySetter::onInterface(std::string interfaceName)
|
||||
{
|
||||
interfaceName_ = interfaceName;
|
||||
interfaceName_ = std::move(interfaceName);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -603,7 +638,7 @@ namespace sdbus {
|
||||
|
||||
inline void PropertySetter::toValue(const sdbus::Variant& value)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when setting a property", EINVAL);
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
proxy_
|
||||
.callMethod("Set")
|
||||
|
||||
@@ -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;
|
||||
|
||||
/*!
|
||||
@@ -66,6 +74,13 @@ namespace sdbus {
|
||||
*/
|
||||
virtual void releaseName(const std::string& name) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Retrieve the unique name of a connection. E.g. ":1.xx"
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual std::string getUniqueName() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Enters the D-Bus processing loop
|
||||
*
|
||||
@@ -103,8 +118,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() const = 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 +250,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_ */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
+94
-61
@@ -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)
|
||||
@@ -82,6 +76,14 @@ void Connection::releaseName(const std::string& name)
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to release bus name", -r);
|
||||
}
|
||||
|
||||
std::string Connection::getUniqueName() const
|
||||
{
|
||||
const char* unique = nullptr;
|
||||
auto r = iface_->sd_bus_get_unique_name(bus_.get(), &unique);
|
||||
SDBUS_THROW_ERROR_IF(r < 0 || unique == nullptr, "Failed to get unique bus name", -r);
|
||||
return unique;
|
||||
}
|
||||
|
||||
void Connection::enterProcessingLoop()
|
||||
{
|
||||
while (true)
|
||||
@@ -108,6 +110,15 @@ void Connection::leaveProcessingLoop()
|
||||
joinWithProcessingLoop();
|
||||
}
|
||||
|
||||
sdbus::IConnection::PollData Connection::getProcessLoopPollData() const
|
||||
{
|
||||
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 +147,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 +288,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 +312,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 +337,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 +350,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 +387,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 +426,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 +441,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 +452,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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+30
-17
@@ -35,6 +35,8 @@
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
|
||||
@@ -43,24 +45,31 @@ 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;
|
||||
void releaseName(const std::string& name) override;
|
||||
std::string getUniqueName() const override;
|
||||
void enterProcessingLoop() override;
|
||||
void enterProcessingLoopAsync() override;
|
||||
void leaveProcessingLoop() override;
|
||||
sdbus::IConnection::PollData getProcessLoopPollData() const 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 +104,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 +117,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,209 +0,0 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file ConvenienceApiClasses.cpp
|
||||
*
|
||||
* Created on: Jan 19, 2017
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "sdbus-c++/ConvenienceApiClasses.h"
|
||||
#include "sdbus-c++/IObject.h"
|
||||
#include "sdbus-c++/IProxy.h"
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
MethodRegistrator::MethodRegistrator(IObject& object, const std::string& methodName)
|
||||
: object_(object)
|
||||
, methodName_(methodName)
|
||||
, exceptions_(std::uncaught_exceptions()) // Needs C++17
|
||||
{
|
||||
}
|
||||
|
||||
MethodRegistrator::~MethodRegistrator() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't register the method if MethodRegistrator threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus method", EINVAL);
|
||||
SDBUS_THROW_ERROR_IF(!methodCallback_, "Method handler not specified when registering a DBus method", EINVAL);
|
||||
|
||||
// registerMethod() can throw. But as the MethodRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow registerMethod() 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.
|
||||
object_.registerMethod(interfaceName_, methodName_, inputSignature_, outputSignature_, std::move(methodCallback_), flags_);
|
||||
}
|
||||
|
||||
SignalRegistrator::SignalRegistrator(IObject& object, const std::string& signalName)
|
||||
: object_(object)
|
||||
, signalName_(signalName)
|
||||
, exceptions_(std::uncaught_exceptions()) // Needs C++17
|
||||
{
|
||||
}
|
||||
|
||||
SignalRegistrator::~SignalRegistrator() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't register the signal if SignalRegistrator threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus signal", EINVAL);
|
||||
|
||||
// registerSignal() can throw. But as the SignalRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow registerSignal() 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.
|
||||
object_.registerSignal(interfaceName_, signalName_, signalSignature_, flags_);
|
||||
}
|
||||
|
||||
|
||||
PropertyRegistrator::PropertyRegistrator(IObject& object, const std::string& propertyName)
|
||||
: object_(object)
|
||||
, propertyName_(propertyName)
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
PropertyRegistrator::~PropertyRegistrator() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't register the property if PropertyRegistrator threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus property", EINVAL);
|
||||
|
||||
// registerProperty() can throw. But as the PropertyRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow registerProperty() 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.
|
||||
object_.registerProperty( std::move(interfaceName_)
|
||||
, std::move(propertyName_)
|
||||
, std::move(propertySignature_)
|
||||
, std::move(getter_)
|
||||
, std::move(setter_)
|
||||
, flags_ );
|
||||
}
|
||||
|
||||
|
||||
InterfaceFlagsSetter::InterfaceFlagsSetter(IObject& object, const std::string& interfaceName)
|
||||
: object_(object)
|
||||
, interfaceName_(interfaceName)
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
InterfaceFlagsSetter::~InterfaceFlagsSetter() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't set any flags if InterfaceFlagsSetter threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when setting its flags", EINVAL);
|
||||
|
||||
// setInterfaceFlags() can throw. But as the InterfaceFlagsSetter shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow setInterfaceFlags() 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.
|
||||
object_.setInterfaceFlags( std::move(interfaceName_)
|
||||
, std::move(flags_) );
|
||||
}
|
||||
|
||||
|
||||
SignalEmitter::SignalEmitter(IObject& object, const std::string& signalName)
|
||||
: object_(object)
|
||||
, signalName_(signalName)
|
||||
, exceptions_(std::uncaught_exceptions()) // Needs C++17
|
||||
{
|
||||
}
|
||||
|
||||
SignalEmitter::~SignalEmitter() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't emit the signal if SignalEmitter threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!signal_.isValid(), "DBus interface not specified when emitting a DBus signal", EINVAL);
|
||||
|
||||
// emitSignal() can throw. But as the SignalEmitter shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow emitSignal() 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.
|
||||
object_.emitSignal(signal_);
|
||||
}
|
||||
|
||||
|
||||
MethodInvoker::MethodInvoker(IProxy& proxy, const std::string& methodName)
|
||||
: proxy_(proxy)
|
||||
, methodName_(methodName)
|
||||
, exceptions_(std::uncaught_exceptions()) // Needs C++17
|
||||
{
|
||||
}
|
||||
|
||||
MethodInvoker::~MethodInvoker() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't call the method if it has been called already or if MethodInvoker
|
||||
// threw an exception in one of its methods
|
||||
if (methodCalled_ || std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!method_.isValid(), "DBus interface not specified when calling a DBus method", EINVAL);
|
||||
|
||||
// callMethod() can throw. But as the MethodInvoker shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// 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_);
|
||||
}
|
||||
|
||||
}
|
||||
+7
-2
@@ -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,8 +67,10 @@ 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_get_unique_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;
|
||||
virtual int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) = 0;
|
||||
virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) = 0;
|
||||
@@ -74,8 +81,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
@@ -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); }};
|
||||
|
||||
@@ -134,6 +134,8 @@ sdbus::Signal Object::createSignal(const std::string& interfaceName, const std::
|
||||
|
||||
void Object::emitSignal(const sdbus::Signal& message)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid signal message provided", EINVAL);
|
||||
|
||||
message.send();
|
||||
}
|
||||
|
||||
|
||||
+8
-4
@@ -69,17 +69,21 @@ 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();
|
||||
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid method call message provided", EINVAL);
|
||||
|
||||
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)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid async method call message provided", EINVAL);
|
||||
|
||||
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
@@ -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
|
||||
|
||||
@@ -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_);
|
||||
@@ -151,6 +183,12 @@ int SdBus::sd_bus_release_name(sd_bus *bus, const char *name)
|
||||
return ::sd_bus_release_name(bus, name);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_get_unique_name(sd_bus *bus, const char **name)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
return ::sd_bus_get_unique_name(bus, name);
|
||||
}
|
||||
|
||||
int SdBus::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)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
|
||||
@@ -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,8 +59,10 @@ 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_get_unique_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;
|
||||
virtual int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) override;
|
||||
virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) override;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace
|
||||
, ComplexType
|
||||
> DBusSupportedTypes;
|
||||
|
||||
TYPED_TEST_CASE(Type2DBusTypeSignatureConversion, DBusSupportedTypes);
|
||||
TYPED_TEST_SUITE(Type2DBusTypeSignatureConversion, DBusSupportedTypes);
|
||||
}
|
||||
|
||||
/*-------------------------------------*/
|
||||
|
||||
@@ -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,8 +58,10 @@ 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_METHOD2(sd_bus_get_unique_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));
|
||||
MOCK_METHOD3(sd_bus_add_object_manager, int(sd_bus *bus, sd_bus_slot **slot, const char *path));
|
||||
MOCK_METHOD5(sd_bus_add_match, int(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 << ")";
|
||||
|
||||
Reference in New Issue
Block a user