Compare commits

...

29 Commits

Author SHA1 Message Date
3a76e9c120 Bump revision up to 0.7.4 2019-11-03 20:40:13 +01:00
304b69dd8b Use tag dispatching to construct various types of Connection, refactor Connection unit tests 2019-11-03 20:30:52 +01:00
Jay
099bc857ad Add support for opening a connection to a remote system bus using ssh (#77) 2019-11-03 20:21:39 +01:00
c139110112 Add support for custom timeout value for D-Bus method calls (#72) 2019-11-03 13:54:13 +01:00
e7155c5506 Bump revision up to 0.7.3 2019-10-13 15:39:32 +02:00
0f7de608ac Little code cleanup 2019-10-11 16:04:37 +02:00
c6d4d2710f Skip GetMachineId test when /etc/machine-id is not available 2019-10-11 15:56:00 +02:00
0440dcb15b Added ability to integrate with foreign event loops 2019-10-08 22:09:05 +02:00
e30ce194ab Add getSenderName method to Message 2019-10-06 11:28:16 +02:00
8dea11bac6 Add note on solving potential getent-related Yocto errors 2019-07-09 18:29:58 +02:00
750dab3927 Bump revision up to 0.7.2 2019-07-08 10:01:11 +02:00
bf35157a4a Comment out unused parameter 2019-07-08 09:58:22 +02:00
a09362f79a Switch from plain UnixFd to owning UnixFd (#69) 2019-07-08 09:53:53 +02:00
c264f83e83 Fix Yocto chapter level in the tutorial 2019-06-12 15:03:26 +02:00
71adb5cf30 Add notes on sdbus-c++ Yocto recipes to the tutorial 2019-06-12 15:02:01 +02:00
00177a7e4c Bump revision up to 0.7.1 2019-06-12 09:18:55 +02:00
9826d28f51 Add missing Factory friend to Message subclasses 2019-06-11 20:29:45 +02:00
ab34b0ae50 Update header doxy comments in source files 2019-06-11 20:18:37 +02:00
ff944c9e95 Add protected non-virtual destructor in generated classes 2019-06-10 22:54:16 +02:00
7049d00a78 Remove unnecessary std::move of parameters (thanks to @ardazishvili)
Fixes part of #52
2019-06-10 22:03:02 +02:00
236c10ff56 Resolve a few clang-tidy suggestions and warnings (thanks to @ardazishvili)
Fixes part of #52.
2019-06-10 21:54:02 +02:00
dcad208ffe Redesign inheritance from Message (#62)
... so that the code is more idiomatic, clear and expressive about its intended use
2019-06-10 21:38:30 +02:00
57c840637c Add support for Unix fd D-Bus type 2019-06-10 21:19:56 +02:00
efe799ef3f Update section on standard D-Bus interfaces in the tutorial 2019-06-05 12:34:43 +02:00
5c0a8d5ab4 Add object manager automatically in ObjectManager_adaptor constructor 2019-06-05 12:18:04 +02:00
65b3e7ba00 Update README 2019-06-05 11:43:06 +02:00
b2b0bddf02 Fix Variant signal test in integration tests 2019-06-04 23:45:45 +02:00
11f0edf7b8 Fix fragile time-based waiting in integration tests 2019-06-04 22:48:54 +02:00
946cc8d0cd Fix indentation of doxygen comments 2019-06-04 22:21:49 +02:00
70 changed files with 1882 additions and 963 deletions

View File

@ -4,7 +4,7 @@
cmake_minimum_required(VERSION 3.6)
project(sdbus-c++ VERSION 0.7.0 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}>)

View File

@ -102,3 +102,25 @@ v0.7.0
- Documentation improvements
- Minor changes (renamings) in the code generated by sdbus-c++-xml2cpp generator
- Other minor fixes and internal design improvements
v0.7.1
- Fixed waiting in integration tests
- Added object manager automatically in ObjectManager_adaptor constructor
- Introduced support for unix fd type
- Redesigned Message class hierarchy to be more idiomatic, clean and intent-revealing
- Resolved a few clang-tidy warnings and suggestions
- Extended the tutorial with info on standard D-Bus interfaces
- Added protected non-virtual destructor in generated *_proxy/*_adaptor classes
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

View File

@ -78,10 +78,10 @@ The library is distributed under LGPLv2.1 license.
References/documentation
------------------------
* [Using sdbus-c++](docs/using-sdbus-c++.md) - *the* main, comprehensive tutorial on sdbus-c++
* [Systemd and dbus configuration](docs/systemd-dbus-config.md)
* [D-Bus Specification](https://dbus.freedesktop.org/docs/dbus-specification.html)
* [sd-bus Overview](http://0pointer.net/blog/the-new-sd-bus-api-of-systemd.html)
* [Tutorial: Using sdbus-c++](docs/using-sdbus-c++.md)
* [Systemd and dbus configuration](docs/systemd-dbus-config.md)
Contributing
------------

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})

View File

@ -6,19 +6,20 @@ Using sdbus-c++ library
1. [Introduction](#introduction)
2. [Integrating sdbus-c++ into your project](#integrating-sdbus-c-into-your-project)
3. [Solving libsystemd dependency](#solving-libsystemd-dependency)
4. [Header files and namespaces](#header-files-and-namespaces)
5. [Error signalling and propagation](#error-signalling-and-propagation)
6. [Design of sdbus-c++](#design-of-sdbus-c)
7. [Multiple layers of sdbus-c++ API](#multiple-layers-of-sdbus-c-api)
8. [An example: Number concatenator](#an-example-number-concatenator)
9. [Implementing the Concatenator example using basic sdbus-c++ API layer](#implementing-the-concatenator-example-using-basic-sdbus-c-api-layer)
10. [Implementing the Concatenator example using convenience sdbus-c++ API layer](#implementing-the-concatenator-example-using-convenience-sdbus-c-api-layer)
11. [Implementing the Concatenator example using sdbus-c++-generated stubs](#implementing-the-concatenator-example-using-sdbus-c-generated-stubs)
12. [Asynchronous server-side methods](#asynchronous-server-side-methods)
13. [Asynchronous client-side methods](#asynchronous-client-side-methods)
14. [Using D-Bus properties](#using-d-bus-properties)
15. [Standard D-Bus interfaces](#standard-d-bus-interfaces)
16. [Conclusion](#conclusion)
4. [Distributing sdbus-c++](#distributing-sdbus-c)
5. [Header files and namespaces](#header-files-and-namespaces)
6. [Error signalling and propagation](#error-signalling-and-propagation)
7. [Design of sdbus-c++](#design-of-sdbus-c)
8. [Multiple layers of sdbus-c++ API](#multiple-layers-of-sdbus-c-api)
9. [An example: Number concatenator](#an-example-number-concatenator)
10. [Implementing the Concatenator example using basic sdbus-c++ API layer](#implementing-the-concatenator-example-using-basic-sdbus-c-api-layer)
11. [Implementing the Concatenator example using convenience sdbus-c++ API layer](#implementing-the-concatenator-example-using-convenience-sdbus-c-api-layer)
12. [Implementing the Concatenator example using sdbus-c++-generated stubs](#implementing-the-concatenator-example-using-sdbus-c-generated-stubs)
13. [Asynchronous server-side methods](#asynchronous-server-side-methods)
14. [Asynchronous client-side methods](#asynchronous-client-side-methods)
15. [Using D-Bus properties](#using-d-bus-properties)
16. [Standard D-Bus interfaces](#standard-d-bus-interfaces)
17. [Conclusion](#conclusion)
Introduction
------------
@ -81,10 +82,32 @@ $ ninja libsystemd.so.0.26.0 # or another version number depending which system
### Building and distributing libsystemd as part of sdbus-c++
sdbus-c++ provides `BUILD_LIBSYSTEMD` configuration option. When turned on, sdbus-c++ will automatically download, build and integrate libsystemd as a static library into sdbus-c++ for you. This is the most convenient and effective approach to build, distribute and use sdbus-c++ as a self-contained, systemd-independent library in non-systemd enviroments. Just make sure your build machine has all dependencies needed by libsystemd build process. That includes `meson`, `ninja`, `git` programs and mainly libraries and library headers for `libmount`, `libcap` and `librt` (part of glibc). Also when distributing, make sure these dependency libraries are installed on the production machine. (Contributors willing to help with bringing sdbus-c++ to popular package systems are welcome.)
sdbus-c++ provides `BUILD_LIBSYSTEMD` configuration option. When turned on, sdbus-c++ will automatically download and build libsystemd as a static library and make it an opaque part of sdbus-c++ shared library for you. This is the most convenient and effective approach to build, distribute and use sdbus-c++ as a self-contained, systemd-independent library in non-systemd enviroments. Just make sure your build machine has all dependencies needed by libsystemd build process. That includes `meson`, `ninja`, `git` programs and mainly libraries and library headers for `libmount`, `libcap` and `librt` (part of glibc). Also when distributing, make sure these dependency libraries are installed on the production machine.
You may additionally set the `LIBSYSTEMD_VERSION` configuration flag to fine-tune the version of systemd to be taken in. (The default value is 242).
Distributing sdbus-c++
----------------------
sdbus-c++ recipes for Yocto are available. Contributors willing to help with bringing sdbus-c++ to popular package systems are welcome.
### Yocto
There are sdbus-c++ recipes for already released Yocto versions (for versions Sumo and newer) available in the meta-oe layer of meta-openembedded fork at Kistler-Group:
* [sdbus-c++ recipes for Yocto Sumo](https://github.com/Kistler-Group/meta-openembedded/tree/sumo-with-sdbus-c%2B%2B/meta-oe/recipes-core/sdbus-c%2B%2B)
* [sdbus-c++ recipes for Yocto Thud](https://github.com/Kistler-Group/meta-openembedded/tree/thud-with-sdbus-c%2B%2B/meta-oe/recipes-core/sdbus-c%2B%2B)
* [sdbus-c++ recipes for Yocto Warrior](https://github.com/Kistler-Group/meta-openembedded/tree/warrior-with-sdbus-c%2B%2B/meta-oe/recipes-core/sdbus-c%2B%2B)
Also, there is currently a pull request pending that pushes there recipes upstream -- to the OpenEmbedded project, so they will be officially available for the upcoming Yocto release.
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
---------------------------
@ -132,11 +155,15 @@ The following diagram illustrates the major entities in sdbus-c++.
* invoking remote methods of the corresponding object, in both synchronous and asynchronous way,
* registering handlers for signals,
`Message` class represents a message, which is the fundamental DBus concept. There are three distinctive types of message that derive from the `Message` class:
`Message` class represents a message, which is the fundamental DBus concept. There are three distinctive types of message that are derived from the `Message` class:
* `MethodCall` (with serialized parameters),
* `AsyncMethodCall` (with serialized parameters),
* `MethodReply` (with serialized return values),
* or a `Signal` (with serialized parameters).
* `Signal` (with serialized parameters),
* `PropertySetCall` (with serialized parameter value to be set)
* `PropertyGetReply` (where property value shall be stored)
* `PlainMessage` (for internal purposes).
### Thread safety in sdbus-c++
@ -521,7 +548,7 @@ After running this through the stubs generator, we get the stub code that is des
### concatenator-server-glue.h
For each interface in the XML IDL file the generator creates one class that represents it. The class is de facto an interface which shall be implemented by inheriting from it. The class' constructor takes care of registering all methods, signals and properties. For each D-Bus method there is a pure virtual member function. These pure virtual functions must be implemented in the child class. For each signal, there is a public function member that emits this signal.
For each interface in the XML IDL file the generator creates one class that represents it. The class is de facto an interface which shall be implemented by the class inheriting it. The class' constructor takes care of registering all methods, signals and properties. For each D-Bus method there is a pure virtual member function. These pure virtual functions must be implemented in the child class. For each signal, there is a public function member that emits this signal.
```cpp
/*
@ -551,6 +578,8 @@ protected:
object_.registerSignal("concatenated").onInterface(INTERFACE_NAME).withParameters<std::string>();
}
~Concatenator_adaptor() = default;
public:
void emitConcatenated(const std::string& concatenatedString)
{
@ -600,6 +629,8 @@ protected:
proxy_.uponSignal("concatenated").onInterface(INTERFACE_NAME).call([this](const std::string& concatenatedString){ this->onConcatenated(concatenatedString); });
}
~Concatenator_proxy() = default;
virtual void onConcatenated(const std::string& concatenatedString) = 0;
public:
@ -1058,12 +1089,14 @@ public:
object_.registerProperty("status").onInterface(INTERFACE_NAME).withGetter([this](){ return this->status(); }).withSetter([this](const uint32_t& value){ this->status(value); });
}
~PropertyProvider_adaptor() = default;
private:
// property getter
virtual uint32_t status() = 0;
// property setter
virtual void status(const uint32_t& value) = 0;
/*...*/
};
#endif
@ -1075,7 +1108,7 @@ The proxy:
class PropertyProvider_proxy
{
/*...*/
public:
// getting the property value
uint32_t status()
@ -1088,7 +1121,7 @@ public:
{
object_.setProperty("status").onInterface(INTERFACE_NAME).toValue(value);
}
/*...*/
};
```
@ -1098,7 +1131,20 @@ When implementing the adaptor, we simply need to provide the body for `status` g
Standard D-Bus interfaces
-------------------------
sdbus-c++ provides pre-generated proxy and adaptor classes for standard D-Bus interfaces (which are `org.freedesktop.DBus.Peer`, `org.freedesktop.DBus.Introspectable`, `org.freedesktop.DBus.Properties`, `org.freedesktop.DBus.ObjectManager`). They can be found in `sdbus/StandardInterfaces.h`. Note that adaptor-side implementations of methods of these interfaces are provided already by the library, we don't need to implement them ourselves. Also note that `org.freedesktop.DBus.ObjectManager` interface needs to be activated explicitly -- by calling `addObjectManager()` on an object/adaptor.
sdbus-c++ provides support for standard D-Bus interfaces. These are:
* `org.freedesktop.DBus.Peer`
* `org.freedesktop.DBus.Introspectable`
* `org.freedesktop.DBus.Properties`
* `org.freedesktop.DBus.ObjectManager`
The implementation of methods that these interfaces define is provided by the library. `Peer`, `Introspectable` and `Properties` are automatically part of interfaces of every D-Bus object. `ObjectManager` is not automatically present and has to be enabled by the client when using `IObject` API. When using generated `ObjectManager_adaptor`, `ObjectManager` is enabled automatically in its constructor.
Pre-generated `*_proxy` and `*_adaptor` convenience classes for these standard interfaces are located in `sdbus-c++/StandardInterfaces.h`. We add them simply as additional parameters of `sdbus::ProxyInterfaces` or `sdbus::AdaptorInterfaces` class template, and our proxy or adaptor class inherits convenience functions from those interface classes.
For example, to conveniently emit a `PropertyChanged` signal under `org.freedesktop.DBus.Properties` interface, we just issue `emitPropertiesChangedSignal` function of our adaptor object.
Note that signals of afore-mentioned standard D-Bus interfaces are not emitted by the library automatically. It's clients who are supposed to emit them.
Conclusion
----------

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file AdaptorInterfaces.h
*
@ -130,30 +131,6 @@ namespace sdbus {
getObject().unregister();
}
/*!
* @brief Adds an ObjectManager interface at the path of this D-Bus object
*
* Creates an ObjectManager interface at the specified object path on
* the connection. This is a convenient way to interrogate a connection
* to see what objects it has.
*
* @throws sdbus::Error in case of failure
*/
void addObjectManager()
{
getObject().addObjectManager();
}
/*!
* @brief Removes an ObjectManager interface from the path of this D-Bus object
*
* @throws sdbus::Error in case of failure
*/
void removeObjectManager()
{
getObject().removeObjectManager();
}
protected:
using base_type = AdaptorInterfaces;
};

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file ConvenienceApiClasses.h
*
@ -31,6 +32,8 @@
#include <sdbus-c++/Flags.h>
#include <string>
#include <type_traits>
#include <chrono>
#include <cstdint>
// Forward declarations
namespace sdbus {
@ -163,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);
@ -171,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_{};
@ -181,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_;
};

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file ConvenienceApiClasses.inl
*
@ -39,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)
@ -148,6 +153,9 @@ namespace sdbus {
return *this;
}
/*** ----------------- ***/
/*** SignalRegistrator ***/
/*** ----------------- ***/
// Moved into the library to isolate from C++17 dependency
/*
@ -202,6 +210,9 @@ namespace sdbus {
return *this;
}
/*** ------------------- ***/
/*** PropertyRegistrator ***/
/*** ------------------- ***/
// Moved into the library to isolate from C++17 dependency
/*
@ -253,10 +264,10 @@ namespace sdbus {
if (propertySignature_.empty())
propertySignature_ = signature_of_function_output_arguments<_Function>::str();
getter_ = [callback = std::forward<_Function>(callback)](Message& msg)
getter_ = [callback = std::forward<_Function>(callback)](PropertyGetReply& reply)
{
// Get the propety value and serialize it into the message
msg << callback();
// Get the propety value and serialize it into the pre-constructed reply message
reply << callback();
};
return *this;
@ -271,14 +282,14 @@ namespace sdbus {
if (propertySignature_.empty())
propertySignature_ = signature_of_function_input_arguments<_Function>::str();
setter_ = [callback = std::forward<_Function>(callback)](Message& msg)
setter_ = [callback = std::forward<_Function>(callback)](PropertySetCall& call)
{
// Default-construct property value
using property_type = function_argument_t<_Function, 0>;
std::decay_t<property_type> property;
// Deserialize property value from the message
msg >> property;
// Deserialize property value from the incoming call message
call >> property;
// Invoke setter with the value
callback(property);
@ -308,6 +319,9 @@ namespace sdbus {
return *this;
}
/*** -------------------- ***/
/*** InterfaceFlagsSetter ***/
/*** -------------------- ***/
// Moved into the library to isolate from C++17 dependency
/*
@ -368,6 +382,9 @@ namespace sdbus {
return *this;
}
/*** ------------- ***/
/*** SignalEmitter ***/
/*** ------------- ***/
// Moved into the library to isolate from C++17 dependency
/*
@ -415,6 +432,9 @@ namespace sdbus {
detail::serialize_pack(signal_, std::forward<_Args>(args)...);
}
/*** ------------- ***/
/*** MethodInvoker ***/
/*** ------------- ***/
// Moved into the library to isolate from C++17 dependency
/*
@ -455,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)
{
@ -470,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...);
@ -483,6 +517,9 @@ namespace sdbus {
method_.dontExpectReply();
}
/*** ------------------ ***/
/*** AsyncMethodInvoker ***/
/*** ------------------ ***/
inline AsyncMethodInvoker::AsyncMethodInvoker(IProxy& proxy, const std::string& methodName)
: proxy_(proxy)
@ -497,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)
{
@ -512,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.
@ -524,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)
@ -562,6 +618,9 @@ namespace sdbus {
});
}
/*** -------------- ***/
/*** PropertyGetter ***/
/*** -------------- ***/
inline PropertyGetter::PropertyGetter(IProxy& proxy, const std::string& propertyName)
: proxy_(proxy)
@ -580,6 +639,9 @@ namespace sdbus {
return var;
}
/*** -------------- ***/
/*** PropertySetter ***/
/*** -------------- ***/
inline PropertySetter::PropertySetter(IProxy& proxy, const std::string& propertyName)
: proxy_(proxy)

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Error.h
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Flags.h
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file IConnection.h
*
@ -26,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 {
@ -45,122 +47,211 @@ namespace sdbus {
class IConnection
{
public:
struct PollData
{
int fd;
short int events;
uint64_t timeout_usec;
};
virtual ~IConnection() = default;
/*!
* @brief Requests D-Bus name on the connection
*
* @param[in] name Name to request
*
* @throws sdbus::Error in case of failure
*/
* @brief Requests D-Bus name on the connection
*
* @param[in] name Name to request
*
* @throws sdbus::Error in case of failure
*/
virtual void requestName(const std::string& name) = 0;
/*!
* @brief Releases D-Bus name on the connection
*
* @param[in] name Name to release
*
* @throws sdbus::Error in case of failure
*/
* @brief Releases D-Bus name on the connection
*
* @param[in] name Name to release
*
* @throws sdbus::Error in case of failure
*/
virtual void releaseName(const std::string& name) = 0;
/*!
* @brief Enters the D-Bus processing loop
*
* The incoming D-Bus messages are processed in the loop. The method
* blocks indefinitely, until unblocked via leaveProcessingLoop.
*
* @throws sdbus::Error in case of failure
*/
* @brief Enters the D-Bus processing loop
*
* The incoming D-Bus messages are processed in the loop. The method
* blocks indefinitely, until unblocked via leaveProcessingLoop.
*
* @throws sdbus::Error in case of failure
*/
virtual void enterProcessingLoop() = 0;
/*!
* @brief Enters the D-Bus processing loop in a separate thread
*
* The same as enterProcessingLoop, except that it doesn't block
* because it runs the loop in a separate thread managed internally.
*/
* @brief Enters the D-Bus processing loop in a separate thread
*
* The same as enterProcessingLoop, except that it doesn't block
* because it runs the loop in a separate thread managed internally.
*/
virtual void enterProcessingLoopAsync() = 0;
/*!
* @brief Leaves the D-Bus processing loop
*
* Ends the previously started processing loop.
*
* @throws sdbus::Error in case of failure
*/
* @brief Leaves the D-Bus processing loop
*
* Ends the previously started processing loop.
*
* @throws sdbus::Error in case of failure
*/
virtual void leaveProcessingLoop() = 0;
/*!
* @brief Adds an ObjectManager at the specified D-Bus object path
*
* Creates an ObjectManager interface at the specified object path on
* the connection. This is a convenient way to interrogate a connection
* to see what objects it has.
*
* @throws sdbus::Error in case of failure
*/
* @brief Adds an ObjectManager at the specified D-Bus object path
*
* Creates an ObjectManager interface at the specified object path on
* the connection. This is a convenient way to interrogate a connection
* to see what objects it has.
*
* @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
*
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
* @brief Creates/opens D-Bus system connection
*
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
std::unique_ptr<sdbus::IConnection> createConnection();
/*!
* @brief Creates/opens D-Bus system connection with a name
*
* @param[in] name Name to request on the connection after its opening
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
* @brief Creates/opens D-Bus system connection with a name
*
* @param[in] name Name to request on the connection after its opening
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
std::unique_ptr<sdbus::IConnection> createConnection(const std::string& name);
/*!
* @brief Creates/opens D-Bus system connection
*
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
* @brief Creates/opens D-Bus system connection
*
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
std::unique_ptr<sdbus::IConnection> createSystemBusConnection();
/*!
* @brief Creates/opens D-Bus system connection with a name
*
* @param[in] name Name to request on the connection after its opening
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
* @brief Creates/opens D-Bus system connection with a name
*
* @param[in] name Name to request on the connection after its opening
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name);
/*!
* @brief Creates/opens D-Bus session connection
*
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
* @brief Creates/opens D-Bus session connection
*
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
std::unique_ptr<sdbus::IConnection> createSessionBusConnection();
/*!
* @brief Creates/opens D-Bus session connection with a name
*
* @param[in] name Name to request on the connection after its opening
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
* @brief Creates/opens D-Bus session connection with a name
*
* @param[in] name Name to request on the connection after its opening
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
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_ */

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file IObject.h
*
@ -61,17 +62,17 @@ namespace sdbus {
virtual ~IObject() = default;
/*!
* @brief Registers method that the object will provide on D-Bus
*
* @param[in] interfaceName Name of an interface that the method will belong to
* @param[in] methodName Name of the method
* @param[in] inputSignature D-Bus signature of method input parameters
* @param[in] outputSignature D-Bus signature of method output parameters
* @param[in] methodCallback Callback that implements the body of the method
* @param[in] flags D-Bus method flags (privileged, deprecated, or no reply)
*
* @throws sdbus::Error in case of failure
*/
* @brief Registers method that the object will provide on D-Bus
*
* @param[in] interfaceName Name of an interface that the method will belong to
* @param[in] methodName Name of the method
* @param[in] inputSignature D-Bus signature of method input parameters
* @param[in] outputSignature D-Bus signature of method output parameters
* @param[in] methodCallback Callback that implements the body of the method
* @param[in] flags D-Bus method flags (privileged, deprecated, or no reply)
*
* @throws sdbus::Error in case of failure
*/
virtual void registerMethod( const std::string& interfaceName
, const std::string& methodName
, const std::string& inputSignature
@ -80,31 +81,31 @@ namespace sdbus {
, Flags flags = {} ) = 0;
/*!
* @brief Registers signal that the object will emit on D-Bus
*
* @param[in] interfaceName Name of an interface that the signal will fall under
* @param[in] signalName Name of the signal
* @param[in] signature D-Bus signature of signal parameters
* @param[in] flags D-Bus signal flags (deprecated)
*
* @throws sdbus::Error in case of failure
*/
* @brief Registers signal that the object will emit on D-Bus
*
* @param[in] interfaceName Name of an interface that the signal will fall under
* @param[in] signalName Name of the signal
* @param[in] signature D-Bus signature of signal parameters
* @param[in] flags D-Bus signal flags (deprecated)
*
* @throws sdbus::Error in case of failure
*/
virtual void registerSignal( const std::string& interfaceName
, const std::string& signalName
, const std::string& signature
, Flags flags = {} ) = 0;
/*!
* @brief Registers read-only property that the object will provide on D-Bus
*
* @param[in] interfaceName Name of an interface that the property will fall under
* @param[in] propertyName Name of the property
* @param[in] signature D-Bus signature of property parameters
* @param[in] getCallback Callback that implements the body of the property getter
* @param[in] flags D-Bus property flags (deprecated, property update behavior)
*
* @throws sdbus::Error in case of failure
*/
* @brief Registers read-only property that the object will provide on D-Bus
*
* @param[in] interfaceName Name of an interface that the property will fall under
* @param[in] propertyName Name of the property
* @param[in] signature D-Bus signature of property parameters
* @param[in] getCallback Callback that implements the body of the property getter
* @param[in] flags D-Bus property flags (deprecated, property update behavior)
*
* @throws sdbus::Error in case of failure
*/
virtual void registerProperty( const std::string& interfaceName
, const std::string& propertyName
, const std::string& signature
@ -112,17 +113,17 @@ namespace sdbus {
, Flags flags = {} ) = 0;
/*!
* @brief Registers read/write property that the object will provide on D-Bus
*
* @param[in] interfaceName Name of an interface that the property will fall under
* @param[in] propertyName Name of the property
* @param[in] signature D-Bus signature of property parameters
* @param[in] getCallback Callback that implements the body of the property getter
* @param[in] setCallback Callback that implements the body of the property setter
* @param[in] flags D-Bus property flags (deprecated, property update behavior)
*
* @throws sdbus::Error in case of failure
*/
* @brief Registers read/write property that the object will provide on D-Bus
*
* @param[in] interfaceName Name of an interface that the property will fall under
* @param[in] propertyName Name of the property
* @param[in] signature D-Bus signature of property parameters
* @param[in] getCallback Callback that implements the body of the property getter
* @param[in] setCallback Callback that implements the body of the property setter
* @param[in] flags D-Bus property flags (deprecated, property update behavior)
*
* @throws sdbus::Error in case of failure
*/
virtual void registerProperty( const std::string& interfaceName
, const std::string& propertyName
, const std::string& signature
@ -131,80 +132,80 @@ namespace sdbus {
, Flags flags = {} ) = 0;
/*!
* @brief Sets flags for a given interface
*
* @param[in] interfaceName Name of an interface whose flags will be set
* @param[in] flags Flags to be set
*
* @throws sdbus::Error in case of failure
*/
* @brief Sets flags for a given interface
*
* @param[in] interfaceName Name of an interface whose flags will be set
* @param[in] flags Flags to be set
*
* @throws sdbus::Error in case of failure
*/
virtual void setInterfaceFlags(const std::string& interfaceName, Flags flags) = 0;
/*!
* @brief Finishes object API registration and publishes the object on the bus
*
* The method exports all up to now registered methods, signals and properties on D-Bus.
* Must be called after all methods, signals and properties have been registered.
*
* @throws sdbus::Error in case of failure
*/
* @brief Finishes object API registration and publishes the object on the bus
*
* The method exports all up to now registered methods, signals and properties on D-Bus.
* Must be called after all methods, signals and properties have been registered.
*
* @throws sdbus::Error in case of failure
*/
virtual void finishRegistration() = 0;
/*!
* @brief Unregisters object's API and removes object from the bus
*
* This method unregisters the object, its interfaces, methods, signals and properties
* from the bus. Unregistration is done automatically also in object's destructor. This
* method makes sense if, in the process of object removal, we need to make sure that
* callbacks are unregistered explicitly before the final destruction of the object instance.
*
* @throws sdbus::Error in case of failure
*/
* @brief Unregisters object's API and removes object from the bus
*
* This method unregisters the object, its interfaces, methods, signals and properties
* from the bus. Unregistration is done automatically also in object's destructor. This
* method makes sense if, in the process of object removal, we need to make sure that
* callbacks are unregistered explicitly before the final destruction of the object instance.
*
* @throws sdbus::Error in case of failure
*/
virtual void unregister() = 0;
/*!
* @brief Creates a signal message
*
* @param[in] interfaceName Name of an interface that the signal belongs under
* @param[in] signalName Name of the signal
* @return A signal message
*
* Serialize signal arguments into the returned message and emit the signal by passing
* the message with serialized arguments to the @c emitSignal function.
* Alternatively, use higher-level API @c emitSignal(const std::string& signalName) defined below.
*
* @throws sdbus::Error in case of failure
*/
* @brief Creates a signal message
*
* @param[in] interfaceName Name of an interface that the signal belongs under
* @param[in] signalName Name of the signal
* @return A signal message
*
* Serialize signal arguments into the returned message and emit the signal by passing
* the message with serialized arguments to the @c emitSignal function.
* Alternatively, use higher-level API @c emitSignal(const std::string& signalName) defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual Signal createSignal(const std::string& interfaceName, const std::string& signalName) = 0;
/*!
* @brief Emits signal for this object path
*
* @param[in] message Signal message to be sent out
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
* @brief Emits signal for this object path
*
* @param[in] message Signal message to be sent out
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual void emitSignal(const sdbus::Signal& message) = 0;
/*!
* @brief Emits PropertyChanged signal for specified properties under a given interface of this object path
*
* @param[in] interfaceName Name of an interface that properties belong to
* @param[in] propNames Names of properties that will be included in the PropertiesChanged signal
*
* @throws sdbus::Error in case of failure
*/
* @brief Emits PropertyChanged signal for specified properties under a given interface of this object path
*
* @param[in] interfaceName Name of an interface that properties belong to
* @param[in] propNames Names of properties that will be included in the PropertiesChanged signal
*
* @throws sdbus::Error in case of failure
*/
virtual void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& propNames) = 0;
/*!
* @brief Emits PropertyChanged signal for all properties on a given interface of this object path
*
* @param[in] interfaceName Name of an interface
*
* @throws sdbus::Error in case of failure
*/
* @brief Emits PropertyChanged signal for all properties on a given interface of this object path
*
* @param[in] interfaceName Name of an interface
*
* @throws sdbus::Error in case of failure
*/
virtual void emitPropertiesChangedSignal(const std::string& interfaceName) = 0;
/*!
@ -259,21 +260,21 @@ namespace sdbus {
virtual void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces) = 0;
/*!
* @brief Adds an ObjectManager interface at the path of this D-Bus object
*
* Creates an ObjectManager interface at the specified object path on
* the connection. This is a convenient way to interrogate a connection
* to see what objects it has.
*
* @throws sdbus::Error in case of failure
*/
* @brief Adds an ObjectManager interface at the path of this D-Bus object
*
* Creates an ObjectManager interface at the specified object path on
* the connection. This is a convenient way to interrogate a connection
* to see what objects it has.
*
* @throws sdbus::Error in case of failure
*/
virtual void addObjectManager() = 0;
/*!
* @brief Removes an ObjectManager interface from the path of this D-Bus object
*
* @throws sdbus::Error in case of failure
*/
* @brief Removes an ObjectManager interface from the path of this D-Bus object
*
* @throws sdbus::Error in case of failure
*/
virtual void removeObjectManager() = 0;
/*!
@ -283,109 +284,111 @@ namespace sdbus {
virtual bool hasObjectManager() const = 0;
/*!
* @brief Provides D-Bus connection used by the object
*
* @return Reference to the D-Bus connection
*/
* @brief Provides D-Bus connection used by the object
*
* @return Reference to the D-Bus connection
*/
virtual sdbus::IConnection& getConnection() const = 0;
/*!
* @brief Registers method that the object will provide on D-Bus
*
* @param[in] methodName Name of the method
* @return A helper object for convenient registration of the method
*
* This is a high-level, convenience way of registering D-Bus methods that abstracts
* from the D-Bus message concept. Method arguments/return value are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the parameters and return type
* of the provided native method implementation callback.
*
* Example of use:
* @code
* object.registerMethod("doFoo").onInterface("com.kistler.foo").implementedAs([this](int value){ return this->doFoo(value); });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
* @brief Registers method that the object will provide on D-Bus
*
* @param[in] methodName Name of the method
* @return A helper object for convenient registration of the method
*
* This is a high-level, convenience way of registering D-Bus methods that abstracts
* from the D-Bus message concept. Method arguments/return value are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the parameters and return type
* of the provided native method implementation callback.
*
* Example of use:
* @code
* object.registerMethod("doFoo").onInterface("com.kistler.foo").implementedAs([this](int value){ return this->doFoo(value); });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
MethodRegistrator registerMethod(const std::string& methodName);
/*!
* @brief Registers signal that the object will provide on D-Bus
*
* @param[in] signalName Name of the signal
* @return A helper object for convenient registration of the signal
*
* This is a high-level, convenience way of registering D-Bus signals that abstracts
* from the D-Bus message concept. Signal arguments are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the provided native parameters.
*
* Example of use:
* @code
* object.registerSignal("paramChange").onInterface("com.kistler.foo").withParameters<std::map<int32_t, std::string>>();
* @endcode
*
* @throws sdbus::Error in case of failure
*/
* @brief Registers signal that the object will provide on D-Bus
*
* @param[in] signalName Name of the signal
* @return A helper object for convenient registration of the signal
*
* This is a high-level, convenience way of registering D-Bus signals that abstracts
* from the D-Bus message concept. Signal arguments are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the provided native parameters.
*
* Example of use:
* @code
* object.registerSignal("paramChange").onInterface("com.kistler.foo").withParameters<std::map<int32_t, std::string>>();
* @endcode
*
* @throws sdbus::Error in case of failure
*/
SignalRegistrator registerSignal(const std::string& signalName);
/*!
* @brief Registers property that the object will provide on D-Bus
*
* @param[in] propertyName Name of the property
* @return A helper object for convenient registration of the property
*
* This is a high-level, convenience way of registering D-Bus properties that abstracts
* from the D-Bus message concept. Property arguments are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the provided native callbacks.
*
* Example of use:
* @code
* object_.registerProperty("state").onInterface("com.kistler.foo").withGetter([this](){ return this->state(); });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
* @brief Registers property that the object will provide on D-Bus
*
* @param[in] propertyName Name of the property
* @return A helper object for convenient registration of the property
*
* This is a high-level, convenience way of registering D-Bus properties that abstracts
* from the D-Bus message concept. Property arguments are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the provided native callbacks.
*
* Example of use:
* @code
* object_.registerProperty("state").onInterface("com.kistler.foo").withGetter([this](){ return this->state(); });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
PropertyRegistrator registerProperty(const std::string& propertyName);
/*!
* @brief Sets flags (annotations) for a given interface
*
* @param[in] interfaceName Name of an interface whose flags will be set
* @return A helper object for convenient setting of Interface flags
*
* This is a high-level, convenience alternative to the other setInterfaceFlags overload.
*
* Example of use:
* @code
* object_.setInterfaceFlags("com.kistler.foo").markAsDeprecated().withPropertyUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL);
* @endcode
*
* @throws sdbus::Error in case of failure
*/
* @brief Sets flags (annotations) for a given interface
*
* @param[in] interfaceName Name of an interface whose flags will be set
* @return A helper object for convenient setting of Interface flags
*
* This is a high-level, convenience alternative to the other setInterfaceFlags overload.
*
* Example of use:
* @code
* object_.setInterfaceFlags("com.kistler.foo").markAsDeprecated().withPropertyUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL);
* @endcode
*
* @throws sdbus::Error in case of failure
*/
InterfaceFlagsSetter setInterfaceFlags(const std::string& interfaceName);
/*!
* @brief Emits signal on D-Bus
*
* @param[in] signalName Name of the signal
* @return A helper object for convenient emission of signals
*
* This is a high-level, convenience way of emitting D-Bus signals that abstracts
* from the D-Bus message concept. Signal arguments are automatically serialized
* in a message and D-Bus signatures automatically deduced from the provided native arguments.
*
* Example of use:
* @code
* int arg1 = ...;
* double arg2 = ...;
* object_.emitSignal("fooSignal").onInterface("com.kistler.foo").withArguments(arg1, arg2);
* @endcode
*
* @throws sdbus::Error in case of failure
*/
* @brief Emits signal on D-Bus
*
* @param[in] signalName Name of the signal
* @return A helper object for convenient emission of signals
*
* This is a high-level, convenience way of emitting D-Bus signals that abstracts
* from the D-Bus message concept. Signal arguments are automatically serialized
* in a message and D-Bus signatures automatically deduced from the provided native arguments.
*
* Example of use:
* @code
* int arg1 = ...;
* double arg2 = ...;
* object_.emitSignal("fooSignal").onInterface("com.kistler.foo").withArguments(arg1, arg2);
* @endcode
*
* @throws sdbus::Error in case of failure
*/
SignalEmitter emitSignal(const std::string& signalName);
};
// Out-of-line member definitions
inline MethodRegistrator IObject::registerMethod(const std::string& methodName)
{
return MethodRegistrator(*this, methodName);
@ -393,17 +396,17 @@ namespace sdbus {
inline SignalRegistrator IObject::registerSignal(const std::string& signalName)
{
return SignalRegistrator(*this, std::move(signalName));
return SignalRegistrator(*this, signalName);
}
inline PropertyRegistrator IObject::registerProperty(const std::string& propertyName)
{
return PropertyRegistrator(*this, std::move(propertyName));
return PropertyRegistrator(*this, propertyName);
}
inline InterfaceFlagsSetter IObject::setInterfaceFlags(const std::string& interfaceName)
{
return InterfaceFlagsSetter(*this, std::move(interfaceName));
return InterfaceFlagsSetter(*this, interfaceName);
}
inline SignalEmitter IObject::emitSignal(const std::string& signalName)
@ -412,23 +415,23 @@ namespace sdbus {
}
/*!
* @brief Creates instance representing a D-Bus object
*
* @param[in] connection D-Bus connection to be used by the object
* @param[in] objectPath Path of the D-Bus object
* @return Pointer to the object representation instance
*
* The provided connection will be used by the object to export methods,
* issue signals and provide properties.
*
* Creating a D-Bus object instance is (thread-)safe even upon the connection
* which is already running its processing loop.
*
* Code example:
* @code
* auto proxy = sdbus::createObject(connection, "/com/kistler/foo");
* @endcode
*/
* @brief Creates instance representing a D-Bus object
*
* @param[in] connection D-Bus connection to be used by the object
* @param[in] objectPath Path of the D-Bus object
* @return Pointer to the object representation instance
*
* The provided connection will be used by the object to export methods,
* issue signals and provide properties.
*
* Creating a D-Bus object instance is (thread-)safe even upon the connection
* which is already running its processing loop.
*
* Code example:
* @code
* auto proxy = sdbus::createObject(connection, "/com/kistler/foo");
* @endcode
*/
std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, std::string objectPath);
}

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file IProxy.h
*
@ -30,6 +31,7 @@
#include <string>
#include <memory>
#include <functional>
#include <chrono>
// Forward declarations
namespace sdbus {
@ -61,208 +63,238 @@ namespace sdbus {
virtual ~IProxy() = default;
/*!
* @brief Creates a method call message
*
* @param[in] interfaceName Name of an interface that provides a given method
* @param[in] methodName Name of the method
* @return A method call message
*
* Serialize method arguments into the returned message and invoke the method by passing
* the message with serialized arguments to the @c callMethod function.
* Alternatively, use higher-level API @c callMethod(const std::string& methodName) defined below.
*
* @throws sdbus::Error in case of failure
*/
* @brief Creates a method call message
*
* @param[in] interfaceName Name of an interface that provides a given method
* @param[in] methodName Name of the method
* @return A method call message
*
* Serialize method arguments into the returned message and invoke the method by passing
* the message with serialized arguments to the @c callMethod function.
* Alternatively, use higher-level API @c callMethod(const std::string& methodName) defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) = 0;
/*!
* @brief Creates an asynchronous method call message
*
* @param[in] interfaceName Name of an interface that provides a given method
* @param[in] methodName Name of the method
* @return A method call message
*
* Serialize method arguments into the returned message and invoke the method by passing
* the message with serialized arguments to the @c callMethod function.
* Alternatively, use higher-level API @c callMethodAsync(const std::string& methodName) defined below.
*
* @throws sdbus::Error in case of failure
*/
* @brief Creates an asynchronous method call message
*
* @param[in] interfaceName Name of an interface that provides a given method
* @param[in] methodName Name of the method
* @return A method call message
*
* Serialize method arguments into the returned message and invoke the method by passing
* the message with serialized arguments to the @c callMethod function.
* Alternatively, use higher-level API @c callMethodAsync(const std::string& methodName) defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual AsyncMethodCall createAsyncMethodCall(const std::string& interfaceName, const std::string& methodName) = 0;
/*!
* @brief Calls method on the proxied D-Bus object
*
* @param[in] message Message representing a method call
* @return A method reply message
*
* Normally, the call is blocking, i.e. it waits for the remote method to finish with either
* a return value or an error.
*
* If the method call argument is set to not expect reply, the call will not wait for the remote
* method to finish, i.e. the call will be non-blocking, and the function will return an empty,
* invalid MethodReply object (representing void).
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual MethodReply callMethod(const MethodCall& message) = 0;
* @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
* a return value or an error.
*
* If the method call argument is set to not expect reply, the call will not wait for the remote
* method to finish, i.e. the call will be non-blocking, and the function will return an empty,
* invalid MethodReply object (representing void).
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual MethodReply callMethod(const MethodCall& message, uint64_t timeout = 0) = 0;
/*!
* @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
*
* 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
* event loop processing thread.
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual void callMethod(const AsyncMethodCall& message, async_reply_handler asyncReplyCallback) = 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 Registers a handler for the desired signal emitted by the proxied D-Bus object
*
* @param[in] interfaceName Name of an interface that the signal belongs to
* @param[in] signalName Name of the signal
* @param[in] signalHandler Callback that implements the body of the signal handler
*
* @throws sdbus::Error in case of failure
*/
* @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
* event loop processing thread.
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
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
*
* @param[in] interfaceName Name of an interface that the signal belongs to
* @param[in] signalName Name of the signal
* @param[in] signalHandler Callback that implements the body of the signal handler
*
* @throws sdbus::Error in case of failure
*/
virtual void registerSignalHandler( const std::string& interfaceName
, const std::string& signalName
, signal_handler signalHandler ) = 0;
/*!
* @brief Finishes the registration of signal handlers
*
* The method physically subscribes to the desired signals.
* Must be called only once, after all signals have been registered already.
*
* @throws sdbus::Error in case of failure
*/
* @brief Finishes the registration of signal handlers
*
* The method physically subscribes to the desired signals.
* Must be called only once, after all signals have been registered already.
*
* @throws sdbus::Error in case of failure
*/
virtual void finishRegistration() = 0;
/*!
* @brief Unregisters proxy's signal handlers and stops receving replies to pending async calls
*
* Unregistration is done automatically also in proxy's destructor. This method makes
* sense if, in the process of proxy removal, we need to make sure that callbacks
* are unregistered explicitly before the final destruction of the proxy instance.
*
* @throws sdbus::Error in case of failure
*/
* @brief Unregisters proxy's signal handlers and stops receving replies to pending async calls
*
* Unregistration is done automatically also in proxy's destructor. This method makes
* sense if, in the process of proxy removal, we need to make sure that callbacks
* are unregistered explicitly before the final destruction of the proxy instance.
*
* @throws sdbus::Error in case of failure
*/
virtual void unregister() = 0;
/*!
* @brief Calls method on the proxied D-Bus object
*
* @param[in] methodName Name of the method
* @return A helper object for convenient invocation of the method
*
* This is a high-level, convenience way of calling D-Bus methods that abstracts
* from the D-Bus message concept. Method arguments/return value are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the provided native arguments
* and return values.
*
* Example of use:
* @code
* int result, a = ..., b = ...;
* object_.callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
* @endcode
*
* @throws sdbus::Error in case of failure
*/
* @brief Calls method on the proxied D-Bus object
*
* @param[in] methodName Name of the method
* @return A helper object for convenient invocation of the method
*
* This is a high-level, convenience way of calling D-Bus methods that abstracts
* from the D-Bus message concept. Method arguments/return value are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the provided native arguments
* and return values.
*
* Example of use:
* @code
* int result, a = ..., b = ...;
* object_.callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
* @endcode
*
* @throws sdbus::Error in case of failure
*/
MethodInvoker callMethod(const std::string& methodName);
/*!
* @brief Calls method on the proxied D-Bus object asynchronously
*
* @param[in] methodName Name of the method
* @return A helper object for convenient asynchronous invocation of the method
*
* This is a high-level, convenience way of calling D-Bus methods that abstracts
* from the D-Bus message concept. Method arguments/return value are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the provided native arguments
* and return values.
*
* Example of use:
* @code
* int a = ..., b = ...;
* object_.callMethodAsync("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).uponReplyInvoke([](int result)
* {
* std::cout << "Got result of multiplying " << a << " and " << b << ": " << result << std::endl;
* });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
* @brief Calls method on the proxied D-Bus object asynchronously
*
* @param[in] methodName Name of the method
* @return A helper object for convenient asynchronous invocation of the method
*
* This is a high-level, convenience way of calling D-Bus methods that abstracts
* from the D-Bus message concept. Method arguments/return value are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the provided native arguments
* and return values.
*
* Example of use:
* @code
* int a = ..., b = ...;
* object_.callMethodAsync("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).uponReplyInvoke([](int result)
* {
* std::cout << "Got result of multiplying " << a << " and " << b << ": " << result << std::endl;
* });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
AsyncMethodInvoker callMethodAsync(const std::string& methodName);
/*!
* @brief Registers signal handler for a given signal of the proxied D-Bus object
*
* @param[in] signalName Name of the signal
* @return A helper object for convenient registration of the signal handler
*
* This is a high-level, convenience way of registering to D-Bus signals that abstracts
* from the D-Bus message concept. Signal arguments are automatically serialized
* in a message and D-Bus signatures automatically deduced from the parameters
* of the provided native signal callback.
*
* Example of use:
* @code
* object_.uponSignal("fooSignal").onInterface("com.kistler.foo").call([this](int arg1, double arg2){ this->onFooSignal(arg1, arg2); });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
* @brief Registers signal handler for a given signal of the proxied D-Bus object
*
* @param[in] signalName Name of the signal
* @return A helper object for convenient registration of the signal handler
*
* This is a high-level, convenience way of registering to D-Bus signals that abstracts
* from the D-Bus message concept. Signal arguments are automatically serialized
* in a message and D-Bus signatures automatically deduced from the parameters
* of the provided native signal callback.
*
* Example of use:
* @code
* object_.uponSignal("fooSignal").onInterface("com.kistler.foo").call([this](int arg1, double arg2){ this->onFooSignal(arg1, arg2); });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
SignalSubscriber uponSignal(const std::string& signalName);
/*!
* @brief Gets value of a property of the proxied D-Bus object
*
* @param[in] propertyName Name of the property
* @return A helper object for convenient getting of property value
*
* This is a high-level, convenience way of reading D-Bus property values that abstracts
* from the D-Bus message concept. sdbus::Variant is returned which shall then be converted
* to the real property type (implicit conversion is supported).
*
* Example of use:
* @code
* int state = object.getProperty("state").onInterface("com.kistler.foo");
* @endcode
*
* @throws sdbus::Error in case of failure
*/
* @brief Gets value of a property of the proxied D-Bus object
*
* @param[in] propertyName Name of the property
* @return A helper object for convenient getting of property value
*
* This is a high-level, convenience way of reading D-Bus property values that abstracts
* from the D-Bus message concept. sdbus::Variant is returned which shall then be converted
* to the real property type (implicit conversion is supported).
*
* Example of use:
* @code
* int state = object.getProperty("state").onInterface("com.kistler.foo");
* @endcode
*
* @throws sdbus::Error in case of failure
*/
PropertyGetter getProperty(const std::string& propertyName);
/*!
* @brief Sets value of a property of the proxied D-Bus object
*
* @param[in] propertyName Name of the property
* @return A helper object for convenient setting of property value
*
* This is a high-level, convenience way of writing D-Bus property values that abstracts
* from the D-Bus message concept.
*
* Example of use:
* @code
* int state = ...;
* object_.setProperty("state").onInterface("com.kistler.foo").toValue(state);
* @endcode
*
* @throws sdbus::Error in case of failure
*/
* @brief Sets value of a property of the proxied D-Bus object
*
* @param[in] propertyName Name of the property
* @return A helper object for convenient setting of property value
*
* This is a high-level, convenience way of writing D-Bus property values that abstracts
* from the D-Bus message concept.
*
* Example of use:
* @code
* int state = ...;
* object_.setProperty("state").onInterface("com.kistler.foo").toValue(state);
* @endcode
*
* @throws sdbus::Error in case of failure
*/
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);
@ -289,68 +321,68 @@ namespace sdbus {
}
/*!
* @brief Creates a proxy object for a specific remote D-Bus object
*
* @param[in] connection D-Bus connection to be used by the proxy object
* @param[in] destination Bus name that provides the remote D-Bus object
* @param[in] objectPath Path of the remote D-Bus object
* @return Pointer to the proxy object instance
*
* The provided connection will be used by the proxy to issue calls against the object,
* and signals, if any, will be subscribed to on this connection. The caller still
* remains the owner of the connection (the proxy just keeps a reference to it), and
* should make sure that a processing loop is running on that connection, so the proxy
* may receive incoming signals and asynchronous method replies.
*
* Code example:
* @code
* auto proxy = sdbus::createProxy(connection, "com.kistler.foo", "/com/kistler/foo");
* @endcode
*/
* @brief Creates a proxy object for a specific remote D-Bus object
*
* @param[in] connection D-Bus connection to be used by the proxy object
* @param[in] destination Bus name that provides the remote D-Bus object
* @param[in] objectPath Path of the remote D-Bus object
* @return Pointer to the proxy object instance
*
* The provided connection will be used by the proxy to issue calls against the object,
* and signals, if any, will be subscribed to on this connection. The caller still
* remains the owner of the connection (the proxy just keeps a reference to it), and
* should make sure that a processing loop is running on that connection, so the proxy
* may receive incoming signals and asynchronous method replies.
*
* Code example:
* @code
* auto proxy = sdbus::createProxy(connection, "com.kistler.foo", "/com/kistler/foo");
* @endcode
*/
std::unique_ptr<sdbus::IProxy> createProxy( sdbus::IConnection& connection
, std::string destination
, std::string objectPath );
/*!
* @brief Creates a proxy object for a specific remote D-Bus object
*
* @param[in] connection D-Bus connection to be used by the proxy object
* @param[in] destination Bus name that provides the remote D-Bus object
* @param[in] objectPath Path of the remote D-Bus object
* @return Pointer to the object proxy instance
*
* The provided connection will be used by the proxy to issue calls against the object,
* and signals, if any, will be subscribed to on this connection. The Object proxy becomes
* an exclusive owner of this connection, and will automatically start a procesing loop
* upon that connection in a separate internal thread. Handlers for incoming signals and
* asynchronous method replies will be executed in the context of that thread.
*
* Code example:
* @code
* auto proxy = sdbus::createProxy(std::move(connection), "com.kistler.foo", "/com/kistler/foo");
* @endcode
*/
* @brief Creates a proxy object for a specific remote D-Bus object
*
* @param[in] connection D-Bus connection to be used by the proxy object
* @param[in] destination Bus name that provides the remote D-Bus object
* @param[in] objectPath Path of the remote D-Bus object
* @return Pointer to the object proxy instance
*
* The provided connection will be used by the proxy to issue calls against the object,
* and signals, if any, will be subscribed to on this connection. The Object proxy becomes
* an exclusive owner of this connection, and will automatically start a procesing loop
* upon that connection in a separate internal thread. Handlers for incoming signals and
* asynchronous method replies will be executed in the context of that thread.
*
* Code example:
* @code
* auto proxy = sdbus::createProxy(std::move(connection), "com.kistler.foo", "/com/kistler/foo");
* @endcode
*/
std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<sdbus::IConnection>&& connection
, std::string destination
, std::string objectPath );
/*!
* @brief Creates a proxy object for a specific remote D-Bus object
*
* @param[in] destination Bus name that provides the remote D-Bus object
* @param[in] objectPath Path of the remote D-Bus object
* @return Pointer to the object proxy instance
*
* No D-Bus connection is provided here, so the object proxy will create and manage
* his own connection, and will automatically start a procesing loop upon that connection
* in a separate internal thread. Handlers for incoming signals and asynchronous
* method replies will be executed in the context of that thread.
*
* Code example:
* @code
* auto proxy = sdbus::createProxy("com.kistler.foo", "/com/kistler/foo");
* @endcode
*/
* @brief Creates a proxy object for a specific remote D-Bus object
*
* @param[in] destination Bus name that provides the remote D-Bus object
* @param[in] objectPath Path of the remote D-Bus object
* @return Pointer to the object proxy instance
*
* No D-Bus connection is provided here, so the object proxy will create and manage
* his own connection, and will automatically start a procesing loop upon that connection
* in a separate internal thread. Handlers for incoming signals and asynchronous
* method replies will be executed in the context of that thread.
*
* Code example:
* @code
* auto proxy = sdbus::createProxy("com.kistler.foo", "/com/kistler/foo");
* @endcode
*/
std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
, std::string objectPath );

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Message.h
*
@ -43,6 +44,7 @@ namespace sdbus {
class ObjectPath;
class Signature;
template <typename... _ValueTypes> class Struct;
struct UnixFd;
class MethodReply;
namespace internal {
class ISdBus;
@ -76,16 +78,6 @@ namespace sdbus {
class Message
{
public:
Message() = default;
Message(internal::ISdBus* sdbus) noexcept;
Message(void *msg, internal::ISdBus* sdbus) noexcept;
Message(void *msg, internal::ISdBus* sdbus, adopt_message_t) noexcept;
Message(const Message&) noexcept;
Message& operator=(const Message&) noexcept;
Message(Message&& other) noexcept;
Message& operator=(Message&& other) noexcept;
~Message();
Message& operator<<(bool item);
Message& operator<<(int16_t item);
Message& operator<<(int32_t item);
@ -100,6 +92,7 @@ namespace sdbus {
Message& operator<<(const Variant &item);
Message& operator<<(const ObjectPath &item);
Message& operator<<(const Signature &item);
Message& operator<<(const UnixFd &item);
Message& operator>>(bool& item);
Message& operator>>(int16_t& item);
@ -115,6 +108,7 @@ namespace sdbus {
Message& operator>>(Variant &item);
Message& operator>>(ObjectPath &item);
Message& operator>>(Signature &item);
Message& operator>>(UnixFd &item);
Message& openContainer(const std::string& signature);
Message& closeContainer();
@ -134,11 +128,12 @@ namespace sdbus {
Message& enterStruct(const std::string& signature);
Message& exitStruct();
operator bool() const;
explicit operator bool() const;
void clearFlags();
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;
@ -147,6 +142,23 @@ namespace sdbus {
void seal();
void rewind(bool complete);
class Factory;
protected:
Message() = default;
explicit Message(internal::ISdBus* sdbus) noexcept;
Message(void *msg, internal::ISdBus* sdbus) noexcept;
Message(void *msg, internal::ISdBus* sdbus, adopt_message_t) noexcept;
Message(const Message&) noexcept;
Message& operator=(const Message&) noexcept;
Message(Message&& other) noexcept;
Message& operator=(Message&& other) noexcept;
~Message();
friend Factory;
protected:
void* msg_{};
internal::ISdBus* sdbus_{};
@ -155,44 +167,82 @@ namespace sdbus {
class MethodCall : public Message
{
public:
using Message::Message;
MethodReply send() const;
friend Factory;
public:
MethodCall() = default;
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;
};
class AsyncMethodCall : public Message
{
using Message::Message;
friend Factory;
public:
using Slot = std::unique_ptr<void, std::function<void(void*)>>;
using Message::Message;
AsyncMethodCall() = default; // Fixes gcc 6.3 error (default c-tor is not imported in above using declaration)
AsyncMethodCall(MethodCall&& call) noexcept;
Slot send(void* callback, void* userData) const;
AsyncMethodCall() = default;
explicit AsyncMethodCall(MethodCall&& call) noexcept;
Slot send(void* callback, void* userData, uint64_t timeout = 0) const;
};
class MethodReply : public Message
{
public:
using Message::Message;
friend Factory;
public:
MethodReply() = default;
void send() const;
};
class Signal : public Message
{
public:
using Message::Message;
friend Factory;
public:
Signal() = default;
void send() const;
};
class PropertySetCall : public Message
{
using Message::Message;
friend Factory;
public:
PropertySetCall() = default;
};
class PropertyGetReply : public Message
{
using Message::Message;
friend Factory;
public:
PropertyGetReply() = default;
};
class PlainMessage : public Message
{
using Message::Message;
friend Factory;
public:
PlainMessage() = default;
};
template <typename _Element>
inline Message& operator<<(Message& msg, const std::vector<_Element>& items)
{

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file MethodResult.h
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file ProxyInterfaces.h
*
@ -94,14 +95,14 @@ namespace sdbus {
{
public:
/*!
* @brief Creates native-like proxy object instance
*
* @param[in] destination Bus name that provides a D-Bus object
* @param[in] objectPath Path of the D-Bus object
*
* This constructor overload creates a proxy that manages its own D-Bus connection(s).
* For more information on its behavior, consult @ref createProxy(std::string,std::string)
*/
* @brief Creates native-like proxy object instance
*
* @param[in] destination Bus name that provides a D-Bus object
* @param[in] objectPath Path of the D-Bus object
*
* This constructor overload creates a proxy that manages its own D-Bus connection(s).
* For more information on its behavior, consult @ref createProxy(std::string,std::string)
*/
ProxyInterfaces(std::string destination, std::string objectPath)
: ProxyObjectHolder(createProxy(std::move(destination), std::move(objectPath)))
, _Interfaces(getProxy())...
@ -109,15 +110,15 @@ namespace sdbus {
}
/*!
* @brief Creates native-like proxy object instance
*
* @param[in] connection D-Bus connection to be used by the proxy object
* @param[in] destination Bus name that provides a D-Bus object
* @param[in] objectPath Path of the D-Bus object
*
* The proxy created this way just references a D-Bus connection owned and managed by the user.
* For more information on its behavior, consult @ref createProxy(IConnection&,std::string,std::string)
*/
* @brief Creates native-like proxy object instance
*
* @param[in] connection D-Bus connection to be used by the proxy object
* @param[in] destination Bus name that provides a D-Bus object
* @param[in] objectPath Path of the D-Bus object
*
* The proxy created this way just references a D-Bus connection owned and managed by the user.
* For more information on its behavior, consult @ref createProxy(IConnection&,std::string,std::string)
*/
ProxyInterfaces(IConnection& connection, std::string destination, std::string objectPath)
: ProxyObjectHolder(createProxy(connection, std::move(destination), std::move(objectPath)))
, _Interfaces(getProxy())...
@ -125,15 +126,15 @@ namespace sdbus {
}
/*!
* @brief Creates native-like proxy object instance
*
* @param[in] connection D-Bus connection to be used by the proxy object
* @param[in] destination Bus name that provides a D-Bus object
* @param[in] objectPath Path of the D-Bus object
*
* The proxy created this way becomes an owner of the connection.
* For more information on its behavior, consult @ref createProxy(std::unique_ptr<sdbus::IConnection>&&,std::string,std::string)
*/
* @brief Creates native-like proxy object instance
*
* @param[in] connection D-Bus connection to be used by the proxy object
* @param[in] destination Bus name that provides a D-Bus object
* @param[in] objectPath Path of the D-Bus object
*
* The proxy created this way becomes an owner of the connection.
* For more information on its behavior, consult @ref createProxy(std::unique_ptr<sdbus::IConnection>&&,std::string,std::string)
*/
ProxyInterfaces(std::unique_ptr<sdbus::IConnection>&& connection, std::string destination, std::string objectPath)
: ProxyObjectHolder(createProxy(std::move(connection), std::move(destination), std::move(objectPath)))
, _Interfaces(getProxy())...

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file StandardInterfaces.h
*
@ -46,6 +47,8 @@ namespace sdbus {
{
}
~Peer_proxy() = default;
public:
void Ping()
{
@ -74,6 +77,8 @@ namespace sdbus {
{
}
~Introspectable_proxy() = default;
public:
std::string Introspect()
{
@ -106,6 +111,8 @@ namespace sdbus {
});
}
~Properties_proxy() = default;
virtual void onPropertiesChanged( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties
, const std::vector<std::string>& invalidatedProperties ) = 0;
@ -160,6 +167,8 @@ namespace sdbus {
});
}
~ObjectManager_proxy() = default;
virtual void onInterfacesAdded( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties) = 0;
virtual void onInterfacesRemoved( const sdbus::ObjectPath& objectPath
@ -192,6 +201,8 @@ namespace sdbus {
{
}
~Properties_adaptor() = default;
public:
void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& properties)
{
@ -216,8 +227,11 @@ namespace sdbus {
ObjectManager_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.addObjectManager();
}
~ObjectManager_adaptor() = default;
public:
void emitInterfacesAddedSignal()
{

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file TypeTraits.h
*
@ -40,10 +41,12 @@ namespace sdbus {
template <typename... _ValueTypes> class Struct;
class ObjectPath;
class Signature;
class Message;
struct UnixFd;
class MethodCall;
class MethodReply;
class Signal;
class PropertySetCall;
class PropertyGetReply;
template <typename... _Results> class Result;
class Error;
}
@ -53,8 +56,8 @@ namespace sdbus {
using method_callback = std::function<void(MethodCall msg)>;
using async_reply_handler = std::function<void(MethodReply& reply, const Error* error)>;
using signal_handler = std::function<void(Signal& signal)>;
using property_set_callback = std::function<void(Message& msg)>;
using property_get_callback = std::function<void(Message& reply)>;
using property_set_callback = std::function<void(PropertySetCall& msg)>;
using property_get_callback = std::function<void(PropertyGetReply& reply)>;
template <typename _T>
struct signature_of
@ -285,6 +288,17 @@ namespace sdbus {
}
};
template <>
struct signature_of<UnixFd>
{
static constexpr bool is_valid = true;
static const std::string str()
{
return "h";
}
};
template <typename _Element>
struct signature_of<std::vector<_Element>>
{

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Types.h
*
@ -33,6 +34,7 @@
#include <typeinfo>
#include <memory>
#include <tuple>
#include <unistd.h>
namespace sdbus {
@ -94,9 +96,15 @@ namespace sdbus {
std::string peekValueType() const;
private:
mutable Message msg_{};
mutable PlainMessage msg_{};
};
/********************************************//**
* @class Struct
*
* Representation of struct D-Bus type
*
***********************************************/
template <typename... _ValueTypes>
class Struct
: public std::tuple<_ValueTypes...>
@ -135,6 +143,12 @@ namespace sdbus {
return result_type(std::forward<_Elements>(args)...);
}
/********************************************//**
* @class ObjectPath
*
* Representation of object path D-Bus type
*
***********************************************/
class ObjectPath : public std::string
{
public:
@ -146,6 +160,12 @@ namespace sdbus {
using std::string::operator=;
};
/********************************************//**
* @class Signature
*
* Representation of Signature D-Bus type
*
***********************************************/
class Signature : public std::string
{
public:
@ -157,6 +177,106 @@ namespace sdbus {
using std::string::operator=;
};
struct adopt_fd_t { explicit adopt_fd_t() = default; };
#ifdef __cpp_inline_variables
inline constexpr adopt_fd_t adopt_fd{};
#else
constexpr adopt_fd_t adopt_fd{};
#endif
/********************************************//**
* @struct UnixFd
*
* UnixFd is a representation of file descriptor D-Bus type that owns
* the underlying fd, provides access to it, and closes the fd when
* the UnixFd goes out of scope.
*
* UnixFd can be default constructed (owning invalid fd), or constructed from
* an explicitly provided fd by either duplicating or adopting that fd as-is.
*
***********************************************/
class UnixFd
{
public:
UnixFd() = default;
explicit UnixFd(int fd)
: fd_(::dup(fd))
{
}
UnixFd(int fd, adopt_fd_t)
: fd_(fd)
{
}
UnixFd(const UnixFd& other)
{
*this = other;
}
UnixFd& operator=(const UnixFd& other)
{
close();
fd_ = ::dup(other.fd_);
return *this;
}
UnixFd(UnixFd&& other)
{
*this = std::move(other);
}
UnixFd& operator=(UnixFd&& other)
{
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
~UnixFd()
{
close();
}
int get() const
{
return fd_;
}
void reset(int fd = -1)
{
*this = UnixFd{fd};
}
void reset(int fd, adopt_fd_t)
{
*this = UnixFd{fd, adopt_fd};
}
int release()
{
auto fd = fd_;
fd_ = -1;
return fd;
}
bool isValid() const
{
return fd_ >= 0;
}
private:
void close()
{
if (fd_ >= 0)
::close(fd_);
}
int fd_ = -1;
};
}
#endif /* SDBUS_CXX_TYPES_H_ */

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file sdbus-c++.h
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Connection.cpp
*
@ -25,6 +26,7 @@
#include "Connection.h"
#include "SdBus.h"
#include "MessageUtils.h"
#include <sdbus-c++/Message.h>
#include <sdbus-c++/Error.h>
#include "ScopeGuard.h"
@ -33,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)
@ -106,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();
@ -134,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
@ -169,7 +192,7 @@ MethodCall Connection::createMethodCall( const std::string& destination
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method call", -r);
return MethodCall{sdbusMsg, iface_.get(), adopt_message};
return Message::Factory::create<MethodCall>(sdbusMsg, iface_.get(), adopt_message);
}
Signal Connection::createSignal( const std::string& objectPath
@ -186,7 +209,7 @@ Signal Connection::createSignal( const std::string& objectPath
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create signal", -r);
return Signal{sdbusSignal, iface_.get(), adopt_message};
return Message::Factory::create<Signal>(sdbusSignal, iface_.get(), adopt_message);
}
void Connection::emitPropertiesChangedSignal( const std::string& objectPath
@ -257,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)
@ -287,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);
}
@ -326,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;
@ -341,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
@ -381,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 {
@ -399,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)
@ -414,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)
@ -425,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);
}
}

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Connection.h
*
@ -34,6 +35,8 @@
#include <systemd/sd-bus.h>
#include <memory>
#include <thread>
#include <string>
#include <vector>
namespace sdbus { namespace internal {
@ -42,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;
@ -56,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;
@ -94,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
@ -106,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_;
};
}}

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file ConvenienceApiClasses.cpp
*
@ -202,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_);
}
}

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Error.cpp
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Flags.cpp
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file IConnection.h
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file ISdBus.h
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@ -41,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;
@ -53,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;
@ -61,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;
@ -73,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;
};
}}

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Message.cpp
*
@ -210,7 +211,16 @@ Message& Message::operator<<(const ObjectPath &item)
Message& Message::operator<<(const Signature &item)
{
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_SIGNATURE, item.c_str());
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize an Signature value", -r);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a Signature value", -r);
return *this;
}
Message& Message::operator<<(const UnixFd &item)
{
auto fd = item.get();
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UNIX_FD, &fd);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a UnixFd value", -r);
return *this;
}
@ -383,6 +393,20 @@ Message& Message::operator>>(Signature &item)
return *this;
}
Message& Message::operator>>(UnixFd &item)
{
int fd = -1;
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UNIX_FD, &fd);
if (r == 0)
ok_ = false;
SDBUS_THROW_ERROR_IF(r < 0, "Failed to deserialize a UnixFd value", -r);
item.reset(fd);
return *this;
}
Message& Message::openContainer(const std::string& signature)
{
@ -566,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;
@ -583,7 +612,7 @@ bool Message::isValid() const
bool Message::isEmpty() const
{
return sd_bus_message_is_empty((sd_bus_message*)msg_);
return sd_bus_message_is_empty((sd_bus_message*)msg_) != 0;
}
void MethodCall::dontExpectReply()
@ -596,31 +625,31 @@ bool MethodCall::doesntExpectReply() const
{
auto r = sd_bus_message_get_expect_reply((sd_bus_message*)msg_);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get the dont-expect-reply flag", -r);
return r > 0 ? false : true;
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);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method", -r);
return MethodReply{sdbusReply, sdbus_, adopt_message};
return Factory::create<MethodReply>(sdbusReply, sdbus_, adopt_message);
}
MethodReply MethodCall::sendWithNoReply() const
@ -628,7 +657,7 @@ MethodReply MethodCall::sendWithNoReply() const
auto r = sdbus_->sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method with no reply", -r);
return MethodReply{}; // No reply
return Factory::create<MethodReply>(); // No reply
}
MethodReply MethodCall::createReply() const
@ -637,7 +666,7 @@ MethodReply MethodCall::createReply() const
auto r = sdbus_->sd_bus_message_new_method_return((sd_bus_message*)msg_, &sdbusReply);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method reply", -r);
return MethodReply{sdbusReply, sdbus_, adopt_message};
return Factory::create<MethodReply>(sdbusReply, sdbus_, adopt_message);
}
MethodReply MethodCall::createErrorReply(const Error& error) const
@ -650,7 +679,7 @@ MethodReply MethodCall::createErrorReply(const Error& error) const
auto r = sdbus_->sd_bus_message_new_method_error((sd_bus_message*)msg_, &sdbusErrorReply, &sdbusError);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method error reply", -r);
return MethodReply{sdbusErrorReply, sdbus_, adopt_message};
return Factory::create<MethodReply>(sdbusErrorReply, sdbus_, adopt_message);
}
AsyncMethodCall::AsyncMethodCall(MethodCall&& call) noexcept
@ -658,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); }};
@ -680,7 +709,7 @@ void Signal::send() const
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit signal", -r);
}
Message createPlainMessage()
PlainMessage createPlainMessage()
{
int r;
@ -702,7 +731,7 @@ Message createPlainMessage()
thread_local struct BusReferenceKeeper
{
BusReferenceKeeper(sd_bus* bus) : bus_(sd_bus_ref(bus)) { sd_bus_flush(bus_); }
explicit BusReferenceKeeper(sd_bus* bus) : bus_(sd_bus_ref(bus)) { sd_bus_flush(bus_); }
~BusReferenceKeeper() { sd_bus_flush_close_unref(bus_); }
sd_bus* bus_{};
} busReferenceKeeper{bus};
@ -717,7 +746,7 @@ Message createPlainMessage()
r = sd_bus_message_new(bus, &sdbusMsg, _SD_BUS_MESSAGE_TYPE_INVALID);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create a new message", -r);
return Message{sdbusMsg, &sdbus, adopt_message};
return Message::Factory::create<PlainMessage>(sdbusMsg, &sdbus, adopt_message);
}
}

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file MessageUtils.h
*
@ -30,7 +31,35 @@
namespace sdbus
{
Message createPlainMessage();
class Message::Factory
{
public:
template<typename _Msg>
static _Msg create()
{
return _Msg{};
}
template<typename _Msg>
static _Msg create(void *msg)
{
return _Msg{msg};
}
template<typename _Msg>
static _Msg create(void *msg, internal::ISdBus* sdbus)
{
return _Msg{msg, sdbus};
}
template<typename _Msg>
static _Msg create(void *msg, internal::ISdBus* sdbus, adopt_message_t)
{
return _Msg{msg, sdbus, adopt_message};
}
};
PlainMessage createPlainMessage();
}
#endif /* SDBUS_CXX_INTERNAL_MESSAGEUTILS_H_ */

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Object.cpp
*
@ -24,6 +25,7 @@
*/
#include "Object.h"
#include "MessageUtils.h"
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/Message.h>
#include <sdbus-c++/Error.h>
@ -260,7 +262,7 @@ int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData,
auto* object = static_cast<Object*>(userData);
assert(object != nullptr);
MethodCall message{sdbusMessage, &object->connection_.getSdBusInterface()};
auto message = Message::Factory::create<MethodCall>(sdbusMessage, &object->connection_.getSdBusInterface());
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
auto& callback = object->interfaces_[message.getInterfaceName()].methods_[message.getMemberName()].callback_;
@ -298,7 +300,7 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/
return 1;
}
Message reply{sdbusReply, &object->connection_.getSdBusInterface()};
auto reply = Message::Factory::create<PropertyGetReply>(sdbusReply, &object->connection_.getSdBusInterface());
try
{
@ -327,7 +329,7 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
auto& callback = object->interfaces_[interface].properties_[property].setCallback_;
assert(callback);
Message value{sdbusValue, &object->connection_.getSdBusInterface()};
auto value = Message::Factory::create<PropertySetCall>(sdbusValue, &object->connection_.getSdBusInterface());
try
{

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Object.h
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Proxy.cpp
*
@ -25,6 +26,7 @@
#include "Proxy.h"
#include "IConnection.h"
#include "MessageUtils.h"
#include "sdbus-c++/Message.h"
#include "sdbus-c++/IConnection.h"
#include "sdbus-c++/Error.h"
@ -67,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));
}
@ -137,7 +139,7 @@ int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userDat
SCOPE_EXIT{ proxy.pendingAsyncCalls_.removeCall(asyncCallData->slot.get()); };
MethodReply message{sdbusMessage, &proxy.connection_->getSdBusInterface()};
auto message = Message::Factory::create<MethodReply>(sdbusMessage, &proxy.connection_->getSdBusInterface());
const auto* error = sd_bus_message_get_error(sdbusMessage);
if (error == nullptr)
@ -158,7 +160,7 @@ int Proxy::sdbus_signal_callback(sd_bus_message *sdbusMessage, void *userData, s
auto* proxy = static_cast<Proxy*>(userData);
assert(proxy != nullptr);
Signal message{sdbusMessage, &proxy->connection_->getSdBusInterface()};
auto message = Message::Factory::create<Signal>(sdbusMessage, &proxy->connection_->getSdBusInterface());
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
auto& callback = proxy->interfaces_[message.getInterfaceName()].signals_[message.getMemberName()].callback_;

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Proxy.h
*
@ -51,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

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file ScopeGuard.h
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file SdBus.cpp
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@ -25,6 +26,7 @@
*/
#include "SdBus.h"
#include <sdbus-c++/Error.h>
namespace sdbus { namespace internal {
@ -91,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_);
@ -136,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_);

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file SdBus.h
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@ -47,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;
@ -55,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;

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Types.cpp
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file VTableUtils.c
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file VTableUtils.h
*

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

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file AdaptorAndProxy_test.cpp
*
@ -42,7 +43,10 @@
#include <fstream>
#include <future>
#include <unistd.h>
using ::testing::Eq;
using ::testing::DoubleEq;
using ::testing::Gt;
using ::testing::ElementsAre;
using ::testing::SizeIs;
@ -66,7 +70,7 @@ public:
s_connection->releaseName(INTERFACE_NAME);
}
static void waitUntil(std::atomic<bool>& flag, std::chrono::milliseconds timeout = 1s)
static bool waitUntil(std::atomic<bool>& flag, std::chrono::milliseconds timeout = 5s)
{
std::chrono::milliseconds elapsed{};
std::chrono::milliseconds step{5ms};
@ -74,8 +78,10 @@ public:
std::this_thread::sleep_for(step);
elapsed += step;
if (elapsed > timeout)
throw std::runtime_error("Waiting timed out");
return false;
} while (!flag);
return true;
}
private:
@ -206,6 +212,12 @@ TEST_F(SdbusTestObject, CallsMethodWithObjectPathSuccesfully)
ASSERT_THAT(resObjectPath, Eq(OBJECT_PATH_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithUnixFdSuccesfully)
{
auto resUnixFd = m_proxy->getUnixFd();
ASSERT_THAT(resUnixFd.get(), Gt(UNIX_FD_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithComplexTypeSuccesfully)
{
auto resComplex = m_proxy->getComplex();
@ -216,14 +228,32 @@ TEST_F(SdbusTestObject, CallsMultiplyMethodWithNoReplyFlag)
{
m_proxy->multiplyWithNoReply(INT64_VALUE, DOUBLE_VALUE);
for (auto i = 0; i < 100; ++i)
ASSERT_TRUE(waitUntil(m_adaptor->m_wasMultiplyCalled));
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
{
if (m_adaptor->wasMultiplyCalled())
break;
std::this_thread::sleep_for(10ms);
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";
}
ASSERT_TRUE(m_adaptor->wasMultiplyCalled());
ASSERT_THAT(m_adaptor->getMultiplyResult(), Eq(INT64_VALUE * DOUBLE_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodThatThrowsError)
@ -248,13 +278,7 @@ TEST_F(SdbusTestObject, CallsErrorThrowingMethodWithDontExpectReplySet)
{
ASSERT_NO_THROW(m_proxy->throwErrorWithNoReply());
for (auto i = 0; i < 100; ++i)
{
if (m_adaptor->wasThrowErrorCalled())
break;
std::this_thread::sleep_for(10ms);
}
ASSERT_TRUE(m_adaptor->wasThrowErrorCalled());
ASSERT_TRUE(waitUntil(m_adaptor->m_wasThrowErrorCalled));
}
TEST_F(SdbusTestObject, RunsServerSideAsynchoronousMethodAsynchronously)
@ -368,43 +392,53 @@ 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)
{
auto count = m_proxy->getSimpleCallCount();
m_adaptor->simpleSignal();
usleep(10000);
m_adaptor->emitSimpleSignal();
ASSERT_THAT(m_proxy->getSimpleCallCount(), Eq(count + 1));
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
}
TEST_F(SdbusTestObject, EmitsSignalWithMapSuccesfully)
{
m_adaptor->signalWithMap({{0, "zero"}, {1, "one"}});
usleep(10000);
m_adaptor->emitSignalWithMap({{0, "zero"}, {1, "one"}});
auto map = m_proxy->getMap();
ASSERT_THAT(map[0], Eq("zero"));
ASSERT_THAT(map[1], Eq("one"));
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithMap));
ASSERT_THAT(m_proxy->m_mapFromSignal[0], Eq("zero"));
ASSERT_THAT(m_proxy->m_mapFromSignal[1], Eq("one"));
}
TEST_F(SdbusTestObject, EmitsSignalWithVariantSuccesfully)
{
double d = 3.14;
m_adaptor->signalWithVariant(3.14);
usleep(10000);
m_adaptor->emitSignalWithVariant(d);
ASSERT_THAT(m_proxy->getVariantValue(), d);
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithVariant));
ASSERT_THAT(m_proxy->m_variantFromSignal, DoubleEq(d));
}
TEST_F(SdbusTestObject, EmitsSignalWithoutRegistrationSuccesfully)
{
m_adaptor->signalWithoutRegistration({"platform", {"av"}});
usleep(10000);
m_adaptor->emitSignalWithoutRegistration({"platform", {"av"}});
auto signature = m_proxy->getSignatureFromSignal();
ASSERT_THAT(signature["platform"], Eq("av"));
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithSignature));
ASSERT_THAT(m_proxy->m_signatureFromSignal["platform"], Eq("av"));
}
// Properties
@ -437,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());
}
@ -474,7 +515,7 @@ TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForSelectedProperties)
std::atomic<bool> signalReceived{false};
m_proxy->m_onPropertiesChangedHandler = [&signalReceived]( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties
, const std::vector<std::string>& invalidatedProperties )
, const std::vector<std::string>& /*invalidatedProperties*/ )
{
EXPECT_THAT(interfaceName, Eq(INTERFACE_NAME));
EXPECT_THAT(changedProperties, SizeIs(1));
@ -486,7 +527,7 @@ TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForSelectedProperties)
m_proxy->action(DEFAULT_ACTION_VALUE*2);
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME, {"blocking"});
waitUntil(signalReceived);
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForAllProperties)
@ -506,25 +547,11 @@ TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForAllProperties)
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME);
waitUntil(signalReceived);
}
TEST_F(SdbusTestObject, DoesNotProvideObjectManagerInterfaceByDefault)
{
ASSERT_THROW(m_proxy->GetManagedObjects(), sdbus::Error);
}
TEST_F(SdbusTestObject, ProvidesObjectManagerInterfaceWhenExplicitlyAdded)
{
m_adaptor->addObjectManager();
ASSERT_NO_THROW(m_proxy->GetManagedObjects());
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, GetsZeroManagedObjectsIfHasNoSubPathObjects)
{
m_adaptor->addObjectManager();
const auto objectsInterfacesAndProperties = m_proxy->GetManagedObjects();
ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(0));
@ -532,7 +559,6 @@ TEST_F(SdbusTestObject, GetsZeroManagedObjectsIfHasNoSubPathObjects)
TEST_F(SdbusTestObject, GetsManagedObjectsSuccessfully)
{
m_adaptor->addObjectManager();
auto subObject1 = sdbus::createObject(*s_connection, "/sub/path1");
subObject1->registerProperty("aProperty1").onInterface("org.sdbuscpp.integrationtests.iface1").withGetter([]{return uint8_t{123};});
subObject1->finishRegistration();
@ -559,11 +585,10 @@ TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces)
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
signalReceived = true;
};
m_adaptor->addObjectManager(); // ObjectManager interface needs to be activated explicitly
m_adaptor->emitInterfacesAddedSignal({INTERFACE_NAME});
waitUntil(signalReceived);
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
@ -577,11 +602,10 @@ TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3)); // 3 properties under INTERFACE_NAME
signalReceived = true;
};
m_adaptor->addObjectManager(); // ObjectManager interface needs to be activated explicitly
m_adaptor->emitInterfacesAddedSignal();
waitUntil(signalReceived);
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
@ -595,11 +619,10 @@ TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
EXPECT_THAT(interfaces[0], Eq(INTERFACE_NAME));
signalReceived = true;
};
m_adaptor->addObjectManager(); // ObjectManager interface needs to be activated explicitly
m_adaptor->emitInterfacesRemovedSignal({INTERFACE_NAME});
waitUntil(signalReceived);
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
@ -612,9 +635,8 @@ TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
ASSERT_THAT(interfaces, SizeIs(5)); // INTERFACE_NAME + 4 standard interfaces
signalReceived = true;
};
m_adaptor->addObjectManager(); // ObjectManager interface needs to be activated explicitly
m_adaptor->emitInterfacesRemovedSignal();
waitUntil(signalReceived);
ASSERT_TRUE(waitUntil(signalReceived));
}

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Connection_test.cpp
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file TestingAdaptor.h
*
@ -47,24 +48,31 @@ public:
unregisterAdaptor();
}
bool wasMultiplyCalled() const { return m_multiplyCalled; }
double getMultiplyResult() const { return m_multiplyResult; }
bool wasThrowErrorCalled() const { return m_throwErrorCalled; }
protected:
void noArgNoReturn() const {}
void noArgNoReturn() const
{
}
int32_t getInt() const { return INT32_VALUE; }
int32_t getInt() const
{
return INT32_VALUE;
}
std::tuple<uint32_t, std::string> getTuple() const { return std::make_tuple(UINT32_VALUE, STRING_VALUE); }
std::tuple<uint32_t, std::string> getTuple() const
{
return std::make_tuple(UINT32_VALUE, STRING_VALUE);
}
double multiply(const int64_t& a, const double& b) const { return a * b; }
double multiply(const int64_t& a, const double& b) const
{
return a * b;
}
void multiplyWithNoReply(const int64_t& a, const double& b) const
{
m_multiplyResult = a * b;
m_multiplyCalled = true;
m_wasMultiplyCalled = true;
}
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x) const
@ -142,8 +150,18 @@ protected:
}
}
sdbus::Signature getSignature() const { return SIGNATURE_VALUE; }
sdbus::ObjectPath getObjectPath() const { return OBJECT_PATH_VALUE; }
sdbus::Signature getSignature() const
{
return SIGNATURE_VALUE;
}
sdbus::ObjectPath getObjectPath() const
{
return OBJECT_PATH_VALUE;
}
sdbus::UnixFd getUnixFd() const
{
return sdbus::UnixFd{UNIX_FD_VALUE};
}
ComplexType getComplex() const
{
@ -175,25 +193,45 @@ protected:
void throwError() const
{
m_throwErrorCalled = true;
m_wasThrowErrorCalled = true;
throw sdbus::createError(1, "A test error occurred");
}
std::string state() { return m_state; }
uint32_t action() { return m_action; }
void action(const uint32_t& value) { m_action = value; }
bool blocking() { return m_blocking; }
void blocking(const bool& value) { m_blocking = value; }
std::string state()
{
return m_state;
}
uint32_t action()
{
return m_action;
}
void action(const uint32_t& value)
{
m_action = value;
}
bool blocking()
{
return m_blocking;
}
void blocking(const bool& value)
{
m_blocking = value;
}
private:
const std::string m_state{DEFAULT_STATE_VALUE};
uint32_t m_action{DEFAULT_ACTION_VALUE};
bool m_blocking{DEFAULT_BLOCKING_VALUE};
public: // for tests
// For dont-expect-reply method call verifications
mutable std::atomic<bool> m_multiplyCalled{};
mutable std::atomic<bool> m_wasMultiplyCalled{false};
mutable double m_multiplyResult{};
mutable std::atomic<bool> m_throwErrorCalled{};
mutable std::atomic<bool> m_wasThrowErrorCalled{false};
};

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file TestingProxy.h
*
@ -27,6 +28,7 @@
#define SDBUS_CPP_INTEGRATIONTESTS_TESTINGPROXY_H_
#include "proxy-glue.h"
#include <atomic>
class TestingProxy : public sdbus::ProxyInterfaces< ::testing_proxy
, sdbus::Peer_proxy
@ -46,29 +48,33 @@ public:
unregisterProxy();
}
int getSimpleCallCount() const { return m_simpleCallCounter; }
std::map<int32_t, std::string> getMap() const { return m_map; }
double getVariantValue() const { return m_variantValue; }
std::map<std::string, std::string> getSignatureFromSignal() const { return m_signature; }
void installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, const sdbus::Error* err)> handler)
{
m_DoOperationClientSideAsyncReplyHandler = handler;
}
protected:
void onSimpleSignal() override { ++m_simpleCallCounter; }
void onSimpleSignal() override
{
m_gotSimpleSignal = true;
}
void onSignalWithMap(const std::map<int32_t, std::string>& m) override { m_map = m; }
void onSignalWithMap(const std::map<int32_t, std::string>& m) override
{
m_mapFromSignal = m;
m_gotSignalWithMap = true;
}
void onSignalWithVariant(const sdbus::Variant& v) override
{
m_variantValue = v.get<double>();
m_variantFromSignal = v.get<double>();
m_gotSignalWithVariant = true;
}
void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s) override
{
m_signature[std::get<0>(s)] = static_cast<std::string>(std::get<0>(std::get<1>(s)));
m_signatureFromSignal[std::get<0>(s)] = static_cast<std::string>(std::get<0>(std::get<1>(s)));
m_gotSignalWithSignature = true;
}
void onDoOperationReply(uint32_t returnValue, const sdbus::Error* error) override
@ -101,11 +107,14 @@ protected:
}
//private:
public:
int m_simpleCallCounter{};
std::map<int32_t, std::string> m_map;
double m_variantValue;
std::map<std::string, std::string> m_signature;
public: // for tests
std::atomic<bool> m_gotSimpleSignal{false};
std::atomic<bool> m_gotSignalWithMap{false};
std::map<int32_t, std::string> m_mapFromSignal;
std::atomic<bool> m_gotSignalWithVariant{false};
double m_variantFromSignal;
std::atomic<bool> m_gotSignalWithSignature{false};
std::map<std::string, std::string> m_signatureFromSignal;
std::function<void(uint32_t res, const sdbus::Error* err)> m_DoOperationClientSideAsyncReplyHandler;
std::function<void(const std::string&, const std::map<std::string, sdbus::Variant>&, const std::vector<std::string>&)> m_onPropertiesChangedHandler;
@ -113,6 +122,4 @@ public:
std::function<void(const sdbus::ObjectPath&, const std::vector<std::string>&)> m_onInterfacesRemovedHandler;
};
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTINGPROXY_H_ */

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file adaptor-glue.h
*
@ -52,7 +53,6 @@ using ComplexType = std::map<
class testing_adaptor
{
protected:
testing_adaptor(sdbus::IObject& object) :
object_(object)
@ -97,6 +97,7 @@ protected:
object_.registerMethod("getSignature").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getSignature(); });
object_.registerMethod("getObjectPath").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getObjectPath(); });
object_.registerMethod("getUnixFd").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getUnixFd(); });
object_.registerMethod("getComplex").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getComplex(); }).markAsDeprecated();
@ -114,26 +115,27 @@ protected:
object_.registerProperty("action").onInterface(INTERFACE_NAME).withGetter([this](){ return this->action(); }).withSetter([this](const uint32_t& value){ this->action(value); }).withUpdateBehavior(sdbus::Flags::EMITS_INVALIDATION_SIGNAL);
//object_.registerProperty("blocking").onInterface(INTERFACE_NAME)./*withGetter([this](){ return this->blocking(); }).*/withSetter([this](const bool& value){ this->blocking(value); });
object_.registerProperty("blocking").onInterface(INTERFACE_NAME).withGetter([this](){ return this->blocking(); }).withSetter([this](const bool& value){ this->blocking(value); });
}
~testing_adaptor() = default;
public:
void simpleSignal()
void emitSimpleSignal()
{
object_.emitSignal("simpleSignal").onInterface(INTERFACE_NAME);
}
void signalWithMap(const std::map<int32_t, std::string>& map)
void emitSignalWithMap(const std::map<int32_t, std::string>& map)
{
object_.emitSignal("signalWithMap").onInterface(INTERFACE_NAME).withArguments(map);
}
void signalWithVariant(const sdbus::Variant& v)
void emitSignalWithVariant(const sdbus::Variant& v)
{
object_.emitSignal("signalWithVariant").onInterface(INTERFACE_NAME).withArguments(v);
}
void signalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
void emitSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
{
object_.emitSignal("signalWithoutRegistration").onInterface(INTERFACE_NAME).withArguments(s);
}
@ -163,6 +165,7 @@ protected:
virtual void doOperationAsync(uint32_t param, sdbus::Result<uint32_t> result) = 0;
virtual sdbus::Signature getSignature() const = 0;
virtual sdbus::ObjectPath getObjectPath() const = 0;
virtual sdbus::UnixFd getUnixFd() const = 0;
virtual ComplexType getComplex() const = 0;
virtual void throwError() const = 0;
@ -211,6 +214,19 @@ R"delimiter(<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspectio
<arg type="as" name="invalidated_properties"/>
</signal>
</interface>
<interface name="org.freedesktop.DBus.ObjectManager">
<method name="GetManagedObjects">
<arg type="a{oa{sa{sv}}}" name="object_paths_interfaces_and_properties" direction="out"/>
</method>
<signal name="InterfacesAdded">
<arg type="o" name="object_path"/>
<arg type="a{sa{sv}}" name="interfaces_and_properties"/>
</signal>
<signal name="InterfacesRemoved">
<arg type="o" name="object_path"/>
<arg type="as" name="interfaces"/>
</signal>
</interface>
<interface name="org.sdbuscpp.integrationtests">
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
<method name="doOperation">
@ -253,6 +269,9 @@ R"delimiter(<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspectio
<arg type="u" direction="out"/>
<arg type="s" direction="out"/>
</method>
<method name="getUnixFd">
<arg type="h" direction="out"/>
</method>
<method name="multiply">
<arg type="x" direction="in"/>
<arg type="d" direction="in"/>

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file defs.h
*
@ -40,6 +41,7 @@ constexpr const int32_t INT64_VALUE{-1024};
const std::string STRING_VALUE{"sdbus-c++-testing"};
const sdbus::Signature SIGNATURE_VALUE{"a{is}"};
const sdbus::ObjectPath OBJECT_PATH_VALUE{"/"};
const int UNIX_FD_VALUE = 0;
const std::string DEFAULT_STATE_VALUE{"default-state-value"};
const uint32_t DEFAULT_ACTION_VALUE{999};

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file proxy-glue.h
*
@ -45,6 +46,8 @@ protected:
{ this->onSignalWithoutRegistration(s); });
}
~testing_proxy() = default;
virtual void onSimpleSignal() = 0;
virtual void onSignalWithMap(const std::map<int32_t, std::string>& map) = 0;
virtual void onSignalWithVariant(const sdbus::Variant& v) = 0;
@ -133,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;
@ -175,6 +186,13 @@ public:
return result;
}
sdbus::UnixFd getUnixFd()
{
sdbus::UnixFd result;
object_.callMethod("getUnixFd").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
ComplexType getComplex()
{
ComplexType result;

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file sdbus-c++-integration-tests.cpp
*

View File

@ -27,6 +27,8 @@ protected:
object_.registerSignal("dataSignal").onInterface(INTERFACE_NAME).withParameters<std::string>();
}
~perftests_adaptor() = default;
public:
void emitDataSignal(const std::string& data)
{

View File

@ -25,6 +25,8 @@ protected:
proxy_.uponSignal("dataSignal").onInterface(INTERFACE_NAME).call([this](const std::string& data){ this->onDataSignal(data); });
}
~perftests_proxy() = default;
virtual void onDataSignal(const std::string& data) = 0;
public:

View File

@ -27,6 +27,8 @@ protected:
object_.registerMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getCurrentTemperature(); });
}
~thermometer_adaptor() = default;
private:
virtual uint32_t getCurrentTemperature() = 0;

View File

@ -26,6 +26,8 @@ protected:
{
}
~thermometer_proxy() = default;
public:
uint32_t getCurrentTemperature()
{

View File

@ -27,6 +27,8 @@ protected:
object_.registerSignal("concatenatedSignal").onInterface(INTERFACE_NAME).withParameters<std::string>();
}
~concatenator_adaptor() = default;
public:
void emitConcatenatedSignal(const std::string& concatenatedString)
{

View File

@ -26,6 +26,8 @@ protected:
proxy_.uponSignal("concatenatedSignal").onInterface(INTERFACE_NAME).call([this](const std::string& concatenatedString){ this->onConcatenatedSignal(concatenatedString); });
}
~concatenator_proxy() = default;
virtual void onConcatenatedSignal(const std::string& concatenatedString) = 0;
virtual void onConcatenateReply(const std::string& result, const sdbus::Error* error) = 0;

View File

@ -27,6 +27,8 @@ protected:
object_.registerMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getCurrentTemperature(); });
}
~thermometer_adaptor() = default;
private:
virtual uint32_t getCurrentTemperature() = 0;
@ -55,6 +57,8 @@ protected:
object_.registerMethod("destroyDelegateObject").onInterface(INTERFACE_NAME).implementedAs([this](sdbus::Result<>&& result, sdbus::ObjectPath delegate){ this->destroyDelegateObject(std::move(result), std::move(delegate)); }).withNoReply();
}
~factory_adaptor() = default;
private:
virtual void createDelegateObject(sdbus::Result<sdbus::ObjectPath>&& result) = 0;
virtual void destroyDelegateObject(sdbus::Result<>&& result, sdbus::ObjectPath delegate) = 0;

View File

@ -26,6 +26,8 @@ protected:
{
}
~thermometer_proxy() = default;
public:
uint32_t getCurrentTemperature()
{
@ -57,6 +59,8 @@ protected:
{
}
~factory_proxy() = default;
public:
sdbus::ObjectPath createDelegateObject()
{

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Connection_test.cpp
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@ -34,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;
@ -52,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);
}

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Message_test.cpp
*
@ -30,12 +31,13 @@
#include <cstdint>
using ::testing::Eq;
using ::testing::Gt;
using ::testing::DoubleEq;
using namespace std::string_literals;
namespace
{
std::string deserializeString(sdbus::Message& msg)
std::string deserializeString(sdbus::PlainMessage& msg)
{
std::string str;
msg >> str;
@ -49,29 +51,29 @@ namespace
TEST(AMessage, CanBeDefaultConstructed)
{
ASSERT_NO_THROW(sdbus::Message());
ASSERT_NO_THROW(sdbus::PlainMessage());
}
TEST(AMessage, IsInvalidAfterDefaultConstructed)
{
sdbus::Message msg;
sdbus::PlainMessage msg;
ASSERT_FALSE(msg.isValid());
}
TEST(AMessage, IsValidWhenConstructedAsRealMessage)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
ASSERT_TRUE(msg.isValid());
}
TEST(AMessage, CreatesShallowCopyWhenCopyConstructed)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
msg << "I am a string"s;
msg.seal();
sdbus::Message msgCopy = msg;
sdbus::PlainMessage msgCopy = msg;
std::string str;
msgCopy >> str;
@ -82,11 +84,11 @@ TEST(AMessage, CreatesShallowCopyWhenCopyConstructed)
TEST(AMessage, CreatesDeepCopyWhenEplicitlyCopied)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
msg << "I am a string"s;
msg.seal();
sdbus::Message msgCopy{sdbus::createPlainMessage()};
auto msgCopy = sdbus::createPlainMessage();
msg.copyTo(msgCopy, true);
msgCopy.seal(); // Seal to be able to read from it subsequently
msg.rewind(true); // Rewind to the beginning after copying
@ -97,14 +99,14 @@ TEST(AMessage, CreatesDeepCopyWhenEplicitlyCopied)
TEST(AMessage, IsEmptyWhenContainsNoValue)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
ASSERT_TRUE(msg.isEmpty());
}
TEST(AMessage, IsNotEmptyWhenContainsAValue)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
msg << "I am a string"s;
ASSERT_FALSE(msg.isEmpty());
@ -112,7 +114,7 @@ TEST(AMessage, IsNotEmptyWhenContainsAValue)
TEST(AMessage, CanCarryASimpleInteger)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
int dataWritten = 5;
@ -125,9 +127,24 @@ TEST(AMessage, CanCarryASimpleInteger)
ASSERT_THAT(dataRead, Eq(dataWritten));
}
TEST(AMessage, CanCarryAUnixFd)
{
auto msg = sdbus::createPlainMessage();
sdbus::UnixFd dataWritten{0};
msg << dataWritten;
msg.seal();
sdbus::UnixFd dataRead;
msg >> dataRead;
ASSERT_THAT(dataRead.get(), Gt(dataWritten.get()));
}
TEST(AMessage, CanCarryAVariant)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
auto dataWritten = sdbus::Variant((double)3.14);
@ -142,7 +159,7 @@ TEST(AMessage, CanCarryAVariant)
TEST(AMessage, CanCarryACollectionOfEmbeddedVariants)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
auto value = std::vector<sdbus::Variant>{"hello"s, (double)3.14};
auto dataWritten = sdbus::Variant{value};
@ -159,7 +176,7 @@ TEST(AMessage, CanCarryACollectionOfEmbeddedVariants)
TEST(AMessage, CanCarryAnArray)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
std::vector<int64_t> dataWritten{3545342, 43643532, 324325};
@ -174,7 +191,7 @@ TEST(AMessage, CanCarryAnArray)
TEST(AMessage, CanCarryADictionary)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
std::map<int, std::string> dataWritten{{1, "one"}, {2, "two"}};
@ -189,7 +206,7 @@ TEST(AMessage, CanCarryADictionary)
TEST(AMessage, CanCarryAComplexType)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
using ComplexType = std::map<
uint64_t,

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file TypeTraits_test.cpp
*
@ -72,6 +73,7 @@ namespace
TYPE(sdbus::ObjectPath)HAS_DBUS_TYPE_SIGNATURE("o")
TYPE(sdbus::Signature)HAS_DBUS_TYPE_SIGNATURE("g")
TYPE(sdbus::Variant)HAS_DBUS_TYPE_SIGNATURE("v")
TYPE(sdbus::UnixFd)HAS_DBUS_TYPE_SIGNATURE("h")
TYPE(sdbus::Struct<bool>)HAS_DBUS_TYPE_SIGNATURE("(b)")
TYPE(sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>)HAS_DBUS_TYPE_SIGNATURE("(qdsv)")
TYPE(std::vector<int16_t>)HAS_DBUS_TYPE_SIGNATURE("an")
@ -91,10 +93,11 @@ namespace
>
>,
sdbus::Signature,
sdbus::UnixFd,
const char*
>
>;
TYPE(ComplexType)HAS_DBUS_TYPE_SIGNATURE("a{t(a{ya(obva{is})}gs)}")
TYPE(ComplexType)HAS_DBUS_TYPE_SIGNATURE("a{t(a{ya(obva{is})}ghs)}")
typedef ::testing::Types< bool
, uint8_t
@ -110,6 +113,7 @@ namespace
, sdbus::ObjectPath
, sdbus::Signature
, sdbus::Variant
, sdbus::UnixFd
, sdbus::Struct<bool>
, sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>
, std::vector<int16_t>
@ -117,7 +121,7 @@ namespace
, ComplexType
> DBusSupportedTypes;
TYPED_TEST_CASE(Type2DBusTypeSignatureConversion, DBusSupportedTypes);
TYPED_TEST_SUITE(Type2DBusTypeSignatureConversion, DBusSupportedTypes);
}
/*-------------------------------------*/

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Types_test.cpp
*
@ -28,8 +29,10 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <cstdint>
#include <sys/eventfd.h>
using ::testing::Eq;
using ::testing::Gt;
using namespace std::string_literals;
namespace
@ -152,7 +155,7 @@ TEST(ANonEmptyVariant, SerializesSuccessfullyToAMessage)
{
sdbus::Variant variant("a string");
sdbus::Message msg = sdbus::createPlainMessage();
auto msg = sdbus::createPlainMessage();
ASSERT_NO_THROW(variant.serializeTo(msg));
}
@ -161,7 +164,7 @@ TEST(AnEmptyVariant, ThrowsWhenBeingSerializedToAMessage)
{
sdbus::Variant variant;
sdbus::Message msg = sdbus::createPlainMessage();
auto msg = sdbus::createPlainMessage();
ASSERT_THROW(variant.serializeTo(msg), sdbus::Error);
}
@ -172,7 +175,7 @@ TEST(ANonEmptyVariant, SerializesToAndDeserializesFromAMessageSuccessfully)
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello", ANY_DOUBLE), sdbus::make_struct("world", ANY_DOUBLE)}} };
sdbus::Variant variant(value);
sdbus::Message msg = sdbus::createPlainMessage();
auto msg = sdbus::createPlainMessage();
variant.serializeTo(msg);
msg.seal();
sdbus::Variant variant2;
@ -189,7 +192,7 @@ TEST(CopiesOfVariant, SerializeToAndDeserializeFromMessageSuccessfully)
auto variantCopy1{variant};
auto variantCopy2 = variant;
sdbus::Message msg = sdbus::createPlainMessage();
auto msg = sdbus::createPlainMessage();
variant.serializeTo(msg);
variantCopy1.serializeTo(msg);
variantCopy2.serializeTo(msg);
@ -240,3 +243,104 @@ TEST(ASignature, CanBeConstructedFromStdString)
ASSERT_THAT(sdbus::Signature{aSignature}, Eq(aSignature));
}
TEST(AUnixFd, DuplicatesAndOwnsFdUponStandardConstruction)
{
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
EXPECT_THAT(sdbus::UnixFd{fd}.get(), Gt(fd));
EXPECT_THAT(::close(fd), Eq(0));
}
TEST(AUnixFd, AdoptsAndOwnsFdAsIsUponAdoptionConstruction)
{
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
EXPECT_THAT(sdbus::UnixFd(fd, sdbus::adopt_fd).get(), Eq(fd));
EXPECT_THAT(::close(fd), Eq(-1));
}
TEST(AUnixFd, DuplicatesFdUponCopyConstruction)
{
sdbus::UnixFd unixFd(::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK));
sdbus::UnixFd unixFdCopy{unixFd};
EXPECT_THAT(unixFdCopy.get(), Gt(unixFd.get()));
}
TEST(AUnixFd, TakesOverFdUponMoveConstruction)
{
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
sdbus::UnixFd unixFd(fd, sdbus::adopt_fd);
sdbus::UnixFd unixFdNew{std::move(unixFd)};
EXPECT_FALSE(unixFd.isValid());
EXPECT_THAT(unixFdNew.get(), Eq(fd));
}
TEST(AUnixFd, ClosesFdProperlyUponDestruction)
{
int fd, fdCopy;
{
fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
sdbus::UnixFd unixFd(fd, sdbus::adopt_fd);
auto unixFdNew = std::move(unixFd);
auto unixFdCopy = unixFdNew;
fdCopy = unixFdCopy.get();
}
EXPECT_THAT(::close(fd), Eq(-1));
EXPECT_THAT(::close(fdCopy), Eq(-1));
}
TEST(AUnixFd, DoesNotCloseReleasedFd)
{
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
int fdReleased;
{
sdbus::UnixFd unixFd(fd, sdbus::adopt_fd);
fdReleased = unixFd.release();
EXPECT_FALSE(unixFd.isValid());
}
EXPECT_THAT(fd, Eq(fdReleased));
EXPECT_THAT(::close(fd), Eq(0));
}
TEST(AUnixFd, ClosesFdOnReset)
{
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
sdbus::UnixFd unixFd(fd, sdbus::adopt_fd);
unixFd.reset();
EXPECT_FALSE(unixFd.isValid());
EXPECT_THAT(::close(fd), Eq(-1));
}
TEST(AUnixFd, DuplicatesNewFdAndClosesOriginalFdOnReset)
{
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
sdbus::UnixFd unixFd(fd, sdbus::adopt_fd);
auto newFd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
unixFd.reset(newFd);
EXPECT_THAT(unixFd.get(), Gt(newFd));
EXPECT_THAT(::close(fd), Eq(-1));
EXPECT_THAT(::close(newFd), Eq(0));
}
TEST(AUnixFd, TakesOverNewFdAndClosesOriginalFdOnAdoptingReset)
{
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
sdbus::UnixFd unixFd(fd, sdbus::adopt_fd);
auto newFd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
unixFd.reset(newFd, sdbus::adopt_fd);
EXPECT_THAT(unixFd.get(), Eq(newFd));
EXPECT_THAT(::close(fd), Eq(-1));
}

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file SdBusMock.h
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@ -46,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));
@ -54,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));

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file sdbus-c++-unit-tests.cpp
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file AdaptorGenerator.cpp
*
@ -130,6 +131,8 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
<< propertyRegistration
<< tab << "}" << endl << endl;
body << tab << "~" << className << "() = default;" << endl << endl;
if (!signalMethods.empty())
{
body << "public:" << endl << signalMethods;
@ -190,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;

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file AdaptorGenerator.h
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file BaseGenerator.cpp
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file BaseGenerator.h
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file ProxyGenerator.cpp
*
@ -33,7 +34,6 @@
#include <algorithm>
#include <iterator>
using std::endl;
using sdbuscpp::xml::Document;
@ -95,6 +95,9 @@ std::string ProxyGenerator::processInterface(Node& interface) const
body << tab << "{" << endl
<< registration
<< tab << "}" << endl << endl;
body << tab << "~" << className << "() = default;" << endl << endl;
if (!declaration.empty())
body << declaration << endl;
@ -138,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)
{
@ -146,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)
{
@ -153,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;
@ -163,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;
@ -171,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 << ")";

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file ProxyGenerator.h
*

View File

@ -44,6 +44,7 @@ const char *atomic_type_to_string(char t)
{ 'o', "sdbus::ObjectPath" },
{ 'g', "sdbus::Signature" },
{ 'v', "sdbus::Variant" },
{ 'h', "sdbus::UnixFd" },
{ '\0', "" }
};

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file xml2cpp.cpp
*