Compare commits

...

103 Commits

Author SHA1 Message Date
e199dc58cf Dont use deprecated api 2025-02-12 17:23:42 +01:00
ad7731d900 Fix out of memory access in parser 2023-07-04 19:09:53 +02:00
101dd1c2ba Add boost asio integration 2022-10-30 18:35:47 +01:00
299e6940cd Improve ABI 2022-10-30 18:17:51 +01:00
3f825ffb30 Force event loop to re-enter processing to handle queued messages 2022-09-04 23:26:14 +02:00
35bffd0f25 build: don't include libsystemd as shared pkg-config dep when statically linked (#283)
This works whether `BUILD_LIBSYSTEMD` is `ON` or the separate build of
libsystemd picked up by pkg-config is static. The Gentoo Linux package
requires the latter.

This change also handles `BUILD_SHARED_LIBS` being `OFF`. In this case,
the libsystemd dependency does need to be included, even though it is
static.

CMake does not allow you to build shared and static libraries
simultaneously (with a single `add_library` call), but this change
declares the libsystemd dependency as `Requires.private` rather than
omitting it entirely. This is in case someone builds and installs both a
static and shared build and then calls `pkg-config --static sdbus-c++`.
The static build needs to be installed first so that the pkg-config
file from the shared build takes precedence.
2022-09-03 22:54:06 +02:00
e33a890ce7 docs: fix send_destination property (#284) 2022-08-26 16:49:51 +02:00
751c1addc4 chore: version 1.2.0 2022-08-09 09:50:33 +02:00
2991fa4960 refactor: use pseudo D-Bus connection for plain messages 2022-08-09 08:55:29 +02:00
0f2362d8c3 feat: add support for session bus connection at custom address (#273)
* Add methods to initiate custom session bus connection

The new function helper `createSessionBusConnectionWithAddress` allows to create connection to session bus with custom address.

Signed-off-by: Alexander Livenets <a.livenets@gmail.com>

* feat: add support for session bus connection at custom address

Co-authored-by: Stanislav Angelovic <stanislav.angelovic@siemens.com>
2022-08-08 13:54:09 +02:00
e07c1f3981 chore: update doxygen header info 2022-07-05 18:10:05 +02:00
58426966f4 feat: add CMake variable for extra libsystemd config options 2022-06-27 15:01:06 +02:00
4e105081c9 fix: remove executable flag from source files 2022-06-27 12:17:18 +02:00
5ec6027d5f feat: add support for match rules 2022-06-27 12:14:57 +02:00
d864e1dfa4 refactor: rename dont_request_slot tag to floating_slot 2022-06-27 12:05:06 +02:00
b7f3d7c876 refactor: little fixes and reorganizations around Connection class 2022-06-22 18:12:42 +02:00
2a4c241303 docs: add more info on D-Bus security policy file 2022-06-22 18:12:13 +02:00
5e933c3f17 Detects missing type after array declaration. Fixes #253. (#255)
Co-authored-by: Michael Binz <michael.binz@daimler.com>
2022-06-19 23:02:54 +02:00
b5aee6d019 docs: add information about Buildroot package (#252) 2022-06-07 08:45:34 +02:00
5caea3b72b fix: invalid assert on event fd (#243) 2022-02-13 18:48:50 +01:00
b7a9c63ff0 refactor: add validity checks for names and paths (#242) 2022-02-11 21:53:37 +01:00
7f437a6e06 fix(tests): printer for std::chrono in googletest v1.11.0 2022-02-09 11:43:12 +01:00
f492472e9f Enable move for ObjectPath and Signature.
* Since ObjectPath and Signature have a user-declared copy-ctor and copy
  assignment operator the implicit declaration of corresponding move
  operations is disabled. Explicitly add defaulted versions so that
  move operations actually move instead of copy.

* See: https://github.com/Kistler-Group/sdbus-cpp/issues/230
2022-01-11 19:02:37 +01:00
f673e57a47 Fix potential UB in creation of sdbus::Error.
See https://github.com/Kistler-Group/sdbus-cpp/issues/231
2022-01-11 19:01:22 +01:00
bca8e81037 build: version 1.1.0 2021-12-22 13:17:31 +01:00
33ff69ecd2 chore: remove unnecessary googletest CMake file 2021-12-22 13:10:30 +01:00
b8eb0e8ceb fix: minor fixes for async timeout handling 2021-12-21 13:52:14 +01:00
23fdd0ce8f fix: use non-mutating find in signal unregistration 2021-12-20 10:05:24 +01:00
bb0f3f0242 Fix #88: Timeout handling. (#91)
fix timeout handling

* Despite what is documented in sd_bus_get_timeout(3), the timeout
  returned is actually an absolute time point of Linux's CLOCK_MONOTONIC
  clock. Hence, we first have to subtract the current time from the
  timeout in order to get a relative time that can be passed to poll.

* For async call timeouts to reliably work, we need a way to notify the
  event loop of a connection that is currently blocked waiting in poll.
  I.e. assume the event loop thread entered poll with a timeout set to
  T1. Afterwards, the main thread starts an async call C with a timeout
  T2 < T1. In order for C to be canceled after its timeout T1 has
  elapsed, we have to be able to notify the event loop so that it can
  update its poll data.

Co-authored-by: Urs Ritzmann <ursritzmann@protonmail.ch>
Co-authored-by: Lukasz Marcul <lukasz.marcul@onemeter.com>
2021-12-20 10:00:29 +01:00
0b8f2d9752 fix: use correct path to README file for CPack 2021-12-15 12:46:05 +01:00
9b8a15339e test: delete forgotten file 2021-12-14 21:59:46 +01:00
ef4d9bcba2 chore: add note on libsystemd-dev when libsystemd pkgconfig file is not found 2021-12-14 21:35:11 +01:00
41d33117cc Fix #214: Add means to unregister signal handler 2021-12-14 16:48:50 +01:00
442670ec18 codegen: Support chrono literal timeout by ProxyGenerator
Allow to use human readable chrono literals to specify method call
timeout. The change is backward compatbile - if no unit is provided,
the fallback is "us".

Example:
<annotation name="org.freedesktop.DBus.Method.Timeout" value="500ms"/>
2021-12-14 16:47:01 +01:00
b01db13ff7 fix method name in example 2021-12-01 16:18:58 +01:00
dc0f487751 Reword a few tutorial statements slightly 2021-11-22 14:27:28 +01:00
55310659e8 googletest-download: replace master with main 2021-11-16 17:47:25 +01:00
65782bbf43 tools version 1.0.0 2021-10-25 09:02:37 +02:00
125cb1616c version 1.0.0 2021-10-25 09:02:37 +02:00
f025b92d76 ci: pack only the g++ build 2021-10-25 09:00:30 +02:00
33aa5768a5 ci: create and upload deb package 2021-10-25 09:00:30 +02:00
0703324015 ci: update make commands to cmake 2021-10-25 09:00:30 +02:00
f05f63cd48 cpack: remove test component
If -DBUILD_TESTS=ON, then creating debian packages fails
because dpkg-shlibdeps obiously can't find the sdbus-c++ dependency
for the tests package.

We could disable CPACK_DEBIAN_PACKAGE_SHLIBDEPS for the tests package.
Nevertheless I don't think we want a tests package at all.
2021-10-25 09:00:30 +02:00
de9cd46d8a update ChangeLog: v0.8.4 was never released 2021-10-18 14:16:30 +02:00
ca05b1541f fix non-virtual-dtor warning 2021-10-18 11:58:07 +02:00
35176c4988 integrationtests: differentiate BUS_NAME from INTERFACE_NAME
Even though they have the same value, they are something fundamentally different.
Therefore it is extremely confusing if the constant INTERFACE_NAME is passed
where actually a well-known BUS_NAME (destination) should go.
2021-10-18 11:58:07 +02:00
a5ecbbfcec add examples for the ObjectManager API 2021-10-18 11:58:07 +02:00
4e908612ed introduce new ObjectManager API on generated stubs layer
ATTENTION: Breaking Change!
2021-10-18 11:58:07 +02:00
1d930f324e update changelog: v0.9.0 2021-10-15 15:20:46 +02:00
a341754533 add dependency to libsytemd-dev to sdbus-c++-dev package 2021-09-23 20:18:14 +02:00
9cb8b89a01 add cpack to build debian packages, split the packages by components 2021-09-23 20:18:14 +02:00
c422de641a Support Error parameter in signal handlers 2021-09-17 19:25:39 +02:00
d77bb6b869 Fix potential race condition in Object destruction 2021-09-17 19:03:15 +02:00
8320429ef7 chore: add gperf dependency for libsystemd build 2021-09-17 18:31:04 +02:00
a72e17b932 doc: add gperf dependency for libsystemd 2021-09-17 18:28:41 +02:00
b4f5c0f46c Catch sdbus-c++ exceptions flying from Proxy callbacks to libsystemd 2021-07-29 12:37:15 +02:00
6433b38ed1 Add specific sections for tips and notes in the tutorial 2021-07-22 16:01:42 +02:00
a95fcf5693 Make resetting loop thread ID exception-safe 2021-07-22 13:48:43 +02:00
022831b8c3 Avoid propagating msg unpack exceptions to event loop
This change addresses conditions where an exception is thrown by the library upon receipt of a malformed message from an external
source, and propagated up to the event loop with no chance of
interception by the application. This issue is only experienced by
proxy convenience APIs, as low-level APIs allow the application to
unpack the message.

Strategy:
1. For malformed signals received by proxies: ignore the signal.
2. For malformed async method responses, translate the unpack
  exception into an sdbus::Error, and pass it to the caller as expected.
2021-07-20 18:39:36 +02:00
e16ffb1288 Provide access to D-Bus message in high-level API 2021-06-22 11:31:08 +02:00
75ea127374 connection: add createDefaultBusConnection()
This internally calls sd_bus_open(), which automatically selects the
system or session bus connection based on the presence and 
content of a DBUS_STARTER_BUS_TYPE environment variable and
whether the calling process has root privileges.

This option is very helpful when creating services and clients that will use the system bus in production, but connect to a session
for testing.

Additional changes:
* Removed assertions null-checking make_unique() return values.
  make_unique() calls new, and new is expected to throw or abort
  on failure, making the assertions unhelpful.
* Corrected a typo in the ClosesAndUnrefsBusWhenDestructed
  unit test for the system bus (tested the wrong function).
2021-06-21 15:55:23 +02:00
d74365c535 docs: Add information about conan recipe 2021-06-15 15:27:29 +02:00
a5e94f07bf Fix issue #135: Segfault in Message::peekType()
* Add missing nullptr check in Message::peekType().

* According to its specification, sd_bus_message_peek_type() sets a
  given contents parameter to NULL if the next element in a message is
  not a container. Since assigning a nullptr to a std::string has
  undefined behaviour (typically resulting in an invalid memory access),
  Message::peekType() must not assign contentsSig unconditionally.
2021-06-15 15:26:10 +02:00
fa9569fdd9 Issue 133 race in proxy destruct (#177)
* fix construct/destruct order of mutex/data in AsnycCalls

Destroying a mutex which might still be owned leads to undefined behaviour.
It wasn't the case here but using the right order is more resistant to future bugs.

* proxy AsnycCalls: don't leave map in unspecified state

* fix 133: use thread-safe container for proxy interfaces list

* proxy callback payload uses concrete signalData

* proxy: revert adding the wrapper class InterfaceContainer

It's no longer required and simplifies the locking logic.

* Proxy: avoid additional lambda wrapper around signal callbacks

As proposed in
https://github.com/Kistler-Group/sdbus-cpp/pull/177#issuecomment-834439707
option 3.

Still TODO: Avoid relying on std::map's non-invalidatable by adding just
std::unique_ptr<Proxy::InterfaceData::SignalData> to the container.

* proxy: add missing underscore prefix

* proxy: put InterfaceData::SignalData into a unique_ptr

This ways, we avoid relying on std::map's non-invalidatable references.

* proxy: code style: get raw pointer directly

* style: fix code style

Co-authored-by: Stanislav Angelovic <stanislav.angelovic@siemens.com>
2021-06-03 18:53:38 +02:00
118faa58f6 Add API to get message path and message destination (#167)
* Add API to get message path

* Add API to get message destination

* Handle NULL message fields interface and member

Functions sd_bus_message_get_interface() and sd_bus_message_get_member()
can return null in case the message does not use those fields or does
not have them set.
2021-05-07 15:24:21 +02:00
d65744b1fc Enable default construction of PendingAsyncCall (#180)
This is helpful in use cases where a user defined class wants to
store a PendingAsyncCall as a member variable, or in a STL
container.
2021-05-07 15:22:07 +02:00
6df67469ad proxy: add IProxy::getConnection() (#179)
This provides access to the proxy's bus connection so code using
the proxy does not need to store an external reference to it.

A matching function is already available in IObject.
2021-05-06 16:52:02 +02:00
b0a72cbe92 Make Message's setDestination() thread safe 2021-04-29 15:30:56 +00:00
5ee4c61a1b Add API to set signal destination (#168)
With this patch it is possible to create unicast signals.
2021-04-29 17:18:04 +02:00
d46cbba23c Add README and sdbus-c++ tutorial as additional pages in doxydocs (#153)
Co-authored-by: Craig Spannring <craig.spannring@milnxpd001.mti.local>
Co-authored-by: Stanislav Angelovič <angelovic.s@gmail.com>
2021-04-29 16:46:36 +02:00
70778bfae0 Add note on BUILD_SHARED_LIBS CMake flag to README 2021-04-29 16:07:47 +02:00
5f271abc0c Add note on system bus security policy file to the tutorial 2021-04-29 15:52:06 +02:00
bbffcbf49e fix: minor documentation and test improvements (#166)
* fix: minor documentation and test improvements

* doc: add link to tests in standard interfaces tutorial secion

* Update README.md

Co-authored-by: Urs Ritzmann <urs.ritzmann@kistler.com>

* Update docs/using-sdbus-c++.md

Co-authored-by: Urs Ritzmann <urs.ritzmann@kistler.com>

* Use cmake instead of make in build instructions

Co-authored-by: Stanislav Angelovic <stanislav.angelovic@siemens.com>
Co-authored-by: Urs Ritzmann <urs.ritzmann@kistler.com>
2021-04-28 12:05:14 +02:00
dc6d55a282 Provide CMake config and PkgConfig files for tools (#172) 2021-04-28 11:15:47 +02:00
3f54b5e762 comment sd_bus_match_signal() requires libsystemd v237 2021-04-13 15:59:35 +02:00
fe8cdce107 update changelog: v0.8.4 2021-04-13 15:59:35 +02:00
d47e9d1834 fix issue 145, signals are not filtered by sender 2021-04-13 15:59:35 +02:00
b9723850b8 add integrationtest for issue 145 2021-04-13 15:59:35 +02:00
e1008dd8cf reformat CMake linking 2021-04-13 11:02:20 +02:00
fc9f770512 Find and link against pthread
Building on SUSE fails, as std::thread usage requires linking
against pthread:

/usr/bin/c++ -fPIC -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector-strong -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -Werror=return-type -flto=auto -DNDEBUG -O3 -DNDEBUG -flto=auto -Wl,--as-needed -Wl,--no-undefined -Wl,-z,now -shared -Wl,-soname,libsdbus-c++.so.0 -o libsdbus-c++.so.0.8.3 CMakeFiles/sdbus-c++-objlib.dir/src/Connection.cpp.o CMakeFiles/sdbus-c++-objlib.dir/src/Error.cpp.o CMakeFiles/sdbus-c++-objlib.dir/src/Message.cpp.o CMakeFiles/sdbus-c++-objlib.dir/src/Object.cpp.o CMakeFiles/sdbus-c++-objlib.dir/src/Proxy.cpp.o CMakeFiles/sdbus-c++-objlib.dir/src/Types.cpp.o CMakeFiles/sdbus-c++-objlib.dir/src/Flags.cpp.o CMakeFiles/sdbus-c++-objlib.dir/src/VTableUtils.c.o CMakeFiles/sdbus-c++-objlib.dir/src/SdBus.cpp.o  /usr/lib64/libsystemd.so
/usr/lib64/gcc/x86_64-suse-linux/10/../../../../x86_64-suse-linux/bin/ld: /tmp/libsdbus-c++.so.0.8.3.uWoUml.ltrans0.ltrans.o: in function `sdbus::internal::Connection::enterEventLoopAsync()':
<artificial>:(.text+0x2bb): undefined reference to `pthread_create'
/usr/lib64/gcc/x86_64-suse-linux/10/../../../../x86_64-suse-linux/bin/ld: /tmp/libsdbus-c++.so.0.8.3.uWoUml.ltrans0.ltrans.o: in function `non-virtual thunk to sdbus::internal::Connection::enterEventLoopAsync()':
<artificial>:(.text+0x37a): undefined reference to `pthread_create'
collect2: error: ld returned 1 exit status
2021-04-13 10:56:43 +02:00
5e03e78451 feat: add API to get message credentials (#151)
* sdbus-cpp: Add API to get message credentials

Signed-off-by: Alexander Livenets <a.livenets@gmail.com>

* fix: add <sys/types.h> include for gid_t and other types

Co-authored-by: Stanislav Angelovič <angelovic.s@gmail.com>
2021-03-12 14:14:23 +01:00
3f74512f8e cmake: fix building with BUILD_LIBSYSTEMD and ninja generator (#138)
The ninja generator requires the BUILD_BYPRODUCTS specifier to work out the dependencies.
2021-03-12 14:07:10 +01:00
0090ca97ee fix: missing copy assignment operator (#120)
Explicitly list default copy assignment operator otherwise it is deleted in gcc 8.3.
2021-03-12 14:03:25 +01:00
a649a0225e style: fix indentation in CMakeLists.txt 2021-03-12 14:01:02 +01:00
cfb9956de6 Fixes for floating point systemd version such as 243.4 (#128)
```
~/package $ pkg-config --modversion libsystemd
243.4
~/package $
```
Split it to the list and use the first one
2021-03-12 13:26:21 +01:00
fb008445b1 xml2cpp: Add missing EXPAT include dirs (#136)
This patch is required if EXPAT library is installed in non-standard location.
Without 'target_include_directories' cmake will find EXPAT library:
  ...
  -- Found EXPAT: .../lib/libexpat.a (found version "2.2.10")
  ...

But 'xml.cpp' compilation will fail with error:
  ...
  tools/xml2cpp-codegen/xml.cpp:7:10: fatal error: expat.h: No such file or directory
    7 | #include <expat.h>
  ...
2021-03-12 13:17:55 +01:00
1e2a09cccf docs: note expat dependency (#131)
* docs: note expat dependency

* Add description for expat dependency

Co-authored-by: Stanislav Angelovič <angelovic.s@gmail.com>
2021-03-12 13:15:07 +01:00
a9f2043daa Fix examples in the tutorial
Remove sdbus:: namespace in injected-class-name of the template base class in c-tor initialization list
2021-03-10 09:49:25 +01:00
96b2dfff69 Update using-sdbus-c++.md
Fix name of class in destructor
2021-03-09 17:04:43 +01:00
d6fdacafbe Try to first find googletest in the system before downloading it (#125) 2020-11-16 17:05:36 +01:00
b190646aa5 Make a note in README about an exception for LGPL license 2020-10-11 14:27:14 +02:00
1c56f069dd Add license exception for template code in header files 2020-10-11 14:25:14 +02:00
6e8e5aadb6 Bump up to v0.8.3 2020-09-11 22:29:15 +02:00
3b735bf1aa Introduce CI workflow based on GitHub Actions (#114)
This introduces GitHub CI which runs builds and tests in a matrix configuration. It also changes the systemd repo to stable one. And puts the CI badge (plus a few more badges) to the README page.

Fixes #44
2020-07-22 16:35:42 +02:00
2f7b35c5a8 Fix unused variable warnings for release builds 2020-07-21 15:44:16 +02:00
d5867e1197 Fix unused variable warning for release builds 2020-07-21 15:32:31 +02:00
250aa2bbe3 Add additional messages to CMake build for optional parts 2020-07-21 12:12:14 +02:00
e63357b222 Remove non-virtual-dtor warnings by making classes final 2020-07-21 11:12:57 +02:00
2c6be0307f Update CMake configuration flag names 2020-07-19 18:23:19 +02:00
138a437b22 Fix #112: Address a few inconsistencies and make code more idiomatic 2020-07-18 20:47:05 +02:00
cc8d88cc64 Fix GetObjectPath() in integration tests 2020-07-18 20:30:51 +02:00
bded067496 Fix #43: Clean up integration tests 2020-07-18 20:21:47 +02:00
c1c4512f9f Fix build with gcc8.3
sdbus::ObjectPath and sdbus::Signature copy constructors are implicitly
declared as deleted when compiling with Linaro's ARM toolchain [1] gcc8.3.
Explicitly listing the copy constructors for affected classes fixes the problem.

[1] https://www.linaro.org/
2020-07-16 18:53:18 +02:00
99 changed files with 5194 additions and 2333 deletions

82
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,82 @@
name: CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-18.04, ubuntu-20.04]
compiler: [g++, clang]
build: [shared-libsystemd]
include:
- os: ubuntu-20.04
compiler: g++
build: embedded-static-libsystemd
steps:
- uses: actions/checkout@v2
- name: install-libsystemd-toolchain
if: matrix.build == 'embedded-static-libsystemd'
run: |
sudo apt-get update -y
sudo apt-get install -y meson ninja-build libcap-dev libmount-dev m4 gperf
- name: install-libsystemd-dev
if: matrix.build == 'shared-libsystemd'
run: |
sudo apt-get update -y
sudo apt-get install -y libsystemd-dev
- name: install-clang
if: matrix.compiler == 'clang'
run: |
sudo apt-get install -y clang
sudo update-alternatives --remove-all cc
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 10
sudo update-alternatives --remove-all c++
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 10
- name: configure-debug
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-18.04'
run: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-O0 -g -W -Wextra -Wall -Wnon-virtual-dtor -Werror" -DBUILD_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON ..
- name: configure-release
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04'
run: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_CXX_FLAGS="-O3 -DNDEBUG -W -Wextra -Wall -Wnon-virtual-dtor -Werror" -DBUILD_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON ..
- name: configure-with-embedded-libsystemd
if: matrix.build == 'embedded-static-libsystemd'
run: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON -DBUILD_LIBSYSTEMD=ON -DLIBSYSTEMD_VERSION=244 ..
- name: make
run: |
cd build
cmake --build . -j2
- name: verify
run: |
cd build
sudo cmake --build . --target install
ctest
- name: pack
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04'
run: |
cd build
cpack -G DEB
- name: 'Upload Artifact'
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04' && matrix.compiler == 'g++'
uses: actions/upload-artifact@v2
with:
name: "debian-packages-${{ matrix.os }}-${{ matrix.compiler }}"
path: |
build/sdbus-c++*.deb
build/sdbus-c++*.ddeb
retention-days: 10

View File

@ -4,7 +4,7 @@
cmake_minimum_required(VERSION 3.13) cmake_minimum_required(VERSION 3.13)
project(sdbus-c++ VERSION 0.8.2 LANGUAGES C CXX) project(sdbus-c++ VERSION 1.2.0 LANGUAGES C CXX)
include(GNUInstallDirs) # Installation directories for `install` command and pkgconfig file include(GNUInstallDirs) # Installation directories for `install` command and pkgconfig file
@ -19,16 +19,22 @@ if(NOT BUILD_LIBSYSTEMD)
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libsystemd>=236) pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libsystemd>=236)
if(NOT TARGET PkgConfig::Systemd) if(NOT TARGET PkgConfig::Systemd)
message(FATAL_ERROR "libsystemd of version at least 236 is required, but was not found " message(FATAL_ERROR "libsystemd of version at least 236 is required, but was not found "
"(you may turn BUILD_LIBSYSTEMD on for sdbus-c++ to try downloading " "(if you have systemd in your OS, you may want to install package containing pkgconfig "
"and building libsystemd in as part of sdbus-c++ during configuration)") " files for libsystemd library. On Ubuntu, that is libsystemd-dev. "
" Alternatively, you may turn BUILD_LIBSYSTEMD on for sdbus-c++ to download, build "
"and incorporate libsystemd as embedded library within sdbus-c++)")
endif() endif()
add_library(Systemd::Libsystemd ALIAS PkgConfig::Systemd) add_library(Systemd::Libsystemd ALIAS PkgConfig::Systemd)
set(LIBSYSTEMD_VERSION ${Systemd_VERSION}) string(REGEX MATCHALL "([0-9]+)" SYSTEMD_VERSION_LIST "${Systemd_VERSION}")
list(GET SYSTEMD_VERSION_LIST 0 LIBSYSTEMD_VERSION)
message(STATUS "Building with libsystemd v${LIBSYSTEMD_VERSION}")
else() else()
# Build static libsystemd library as an external project # Build static libsystemd library as an external project
include(cmake/LibsystemdExternalProject.cmake) include(cmake/LibsystemdExternalProject.cmake)
endif() endif()
find_package(Threads REQUIRED)
#------------------------------- #-------------------------------
# SOURCE FILES CONFIGURATION # SOURCE FILES CONFIGURATION
#------------------------------- #-------------------------------
@ -52,6 +58,7 @@ set(SDBUSCPP_HDR_SRCS
${SDBUSCPP_SOURCE_DIR}/Connection.h ${SDBUSCPP_SOURCE_DIR}/Connection.h
${SDBUSCPP_SOURCE_DIR}/IConnection.h ${SDBUSCPP_SOURCE_DIR}/IConnection.h
${SDBUSCPP_SOURCE_DIR}/MessageUtils.h ${SDBUSCPP_SOURCE_DIR}/MessageUtils.h
${SDBUSCPP_SOURCE_DIR}/Utils.h
${SDBUSCPP_SOURCE_DIR}/Object.h ${SDBUSCPP_SOURCE_DIR}/Object.h
${SDBUSCPP_SOURCE_DIR}/Proxy.h ${SDBUSCPP_SOURCE_DIR}/Proxy.h
${SDBUSCPP_SOURCE_DIR}/ScopeGuard.h ${SDBUSCPP_SOURCE_DIR}/ScopeGuard.h
@ -74,6 +81,7 @@ set(SDBUSCPP_PUBLIC_HDRS
${SDBUSCPP_INCLUDE_DIR}/Types.h ${SDBUSCPP_INCLUDE_DIR}/Types.h
${SDBUSCPP_INCLUDE_DIR}/TypeTraits.h ${SDBUSCPP_INCLUDE_DIR}/TypeTraits.h
${SDBUSCPP_INCLUDE_DIR}/Flags.h ${SDBUSCPP_INCLUDE_DIR}/Flags.h
${SDBUSCPP_INCLUDE_DIR}/SdbusAsio.h
${SDBUSCPP_INCLUDE_DIR}/sdbus-c++.h) ${SDBUSCPP_INCLUDE_DIR}/sdbus-c++.h)
set(SDBUSCPP_SRCS ${SDBUSCPP_CPP_SRCS} ${SDBUSCPP_HDR_SRCS} ${SDBUSCPP_PUBLIC_HDRS}) set(SDBUSCPP_SRCS ${SDBUSCPP_CPP_SRCS} ${SDBUSCPP_HDR_SRCS} ${SDBUSCPP_PUBLIC_HDRS})
@ -107,7 +115,10 @@ endif()
if(BUILD_LIBSYSTEMD) if(BUILD_LIBSYSTEMD)
add_dependencies(sdbus-c++-objlib LibsystemdBuildProject) add_dependencies(sdbus-c++-objlib LibsystemdBuildProject)
endif() endif()
target_link_libraries(sdbus-c++-objlib PUBLIC Systemd::Libsystemd) target_link_libraries(sdbus-c++-objlib
PUBLIC
Systemd::Libsystemd
Threads::Threads)
add_library(sdbus-c++) add_library(sdbus-c++)
target_include_directories(sdbus-c++ PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> target_include_directories(sdbus-c++ PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
@ -129,9 +140,9 @@ endif()
install(TARGETS ${EXPORT_SET} install(TARGETS ${EXPORT_SET}
EXPORT sdbus-c++-targets EXPORT sdbus-c++-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT libraries RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT runtime
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT static_libraries ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT dev
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SDBUSCPP_INCLUDE_SUBDIR} COMPONENT dev) PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SDBUSCPP_INCLUDE_SUBDIR} COMPONENT dev)
#---------------------------------- #----------------------------------
@ -141,6 +152,7 @@ install(TARGETS ${EXPORT_SET}
option(BUILD_TESTS "Build and install tests (default OFF)" OFF) option(BUILD_TESTS "Build and install tests (default OFF)" OFF)
if(BUILD_TESTS) if(BUILD_TESTS)
message(STATUS "Building with tests")
enable_testing() enable_testing()
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tests") add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tests")
endif() endif()
@ -152,9 +164,21 @@ endif()
option(BUILD_CODE_GEN "Build and install interface stub code generator (default OFF)" OFF) option(BUILD_CODE_GEN "Build and install interface stub code generator (default OFF)" OFF)
if(BUILD_CODE_GEN) if(BUILD_CODE_GEN)
message(STATUS "Building with code generator tool")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tools") add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tools")
endif() endif()
#----------------------------------
# EXAMPLES
#----------------------------------
option(BUILD_EXAMPLES "Build example programs (default OFF)" OFF)
if(BUILD_EXAMPLES)
message(STATUS "Building with examples")
add_subdirectory(examples)
endif()
#---------------------------------- #----------------------------------
# DOCUMENTATION # DOCUMENTATION
#---------------------------------- #----------------------------------
@ -162,9 +186,10 @@ endif()
option(BUILD_DOC "Build documentation for sdbus-c++" ON) option(BUILD_DOC "Build documentation for sdbus-c++" ON)
if(BUILD_DOC) if(BUILD_DOC)
message(STATUS "Building with documentation")
option(BUILD_DOXYGEN_DOC "Build doxygen documentation for sdbus-c++ API" OFF) option(BUILD_DOXYGEN_DOC "Build doxygen documentation for sdbus-c++ API" OFF)
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/docs") add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/docs")
install(FILES README README.md NEWS COPYING ChangeLog AUTHORS DESTINATION ${CMAKE_INSTALL_DOCDIR}) install(FILES README README.md NEWS COPYING ChangeLog AUTHORS DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT doc)
endif() endif()
#---------------------------------- #----------------------------------
@ -186,6 +211,33 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-config.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++
COMPONENT dev) COMPONENT dev)
if(BUILD_SHARED_LIBS AND (BUILD_LIBSYSTEMD OR Systemd_LINK_LIBRARIES MATCHES "/libsystemd\.a(;|$)"))
set(PKGCONFIG_REQS ".private")
else()
set(PKGCONFIG_REQS "")
endif()
configure_file(pkgconfig/sdbus-c++.pc.in pkgconfig/sdbus-c++.pc @ONLY) configure_file(pkgconfig/sdbus-c++.pc.in pkgconfig/sdbus-c++.pc @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/sdbus-c++.pc install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/sdbus-c++.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT dev) DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT dev)
#----------------------------------
# CPack
#----------------------------------
set(CPACK_PACKAGE_VENDOR "Kistler")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "high-level C++ D-Bus library")
set(CPACK_PACKAGE_CONTACT "info@kistler.com")
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
set(CPACK_COMPONENTS_ALL runtime dev doc)
set(CPACK_COMPONENT_DEV_DEPENDS "runtime")
# specific for DEB generator
set(CPACK_DEB_COMPONENT_INSTALL ON)
set(CPACK_DEBIAN_RUNTIME_DEBUGINFO_PACKAGE ON)
set(CPACK_DEBIAN_RUNTIME_PACKAGE_NAME ${PROJECT_NAME})
set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)
set(CPACK_DEBIAN_PACKAGE_SECTION "libs")
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set(CPACK_DEBIAN_ENABLE_COMPONENT_DEPENDS ON)
set(CPACK_DEBIAN_DEV_PACKAGE_DEPENDS "libsystemd-dev (>=236)")
include(CPack)

22
COPYING-LGPL-Exception Normal file
View File

@ -0,0 +1,22 @@
sdbus-c++ LGPL Exception version 1.0
As an additional permission to the GNU Lesser General Public License version
2.1, the object code form of a "work that uses the Library" may incorporate
material from a header file that is part of the Library. You may distribute
such object code under terms of your choice, provided that:
(i) the header files of the Library have not been modified; and
(ii) the incorporated material is limited to numerical parameters, data
structure layouts, accessors, macros, inline functions and
templates; and
(iii) you comply with the terms of Section 6 of the GNU Lesser General
Public License version 2.1.
Moreover, you may apply this exception to a modified version of the Library,
provided that such modification does not involve copying material from the
Library into the modified Library's header files unless such material is
limited to (i) numerical parameters; (ii) data structure layouts;
(iii) accessors; and (iv) small macros, templates and inline functions of
five lines or less in length.
Furthermore, you are not required to apply this additional permission to a
modified version of the Library.

View File

@ -160,3 +160,59 @@ v0.8.2
- Fix integration test cases failing in specific situations - Fix integration test cases failing in specific situations
- Fix build with clang 9.0.1 and libcxx - Fix build with clang 9.0.1 and libcxx
- Fix potential data race in Proxy's condition variable - Fix potential data race in Proxy's condition variable
v0.8.3
- Fix build with gcc 8.3
- Address a few inconsistencies and make code more idiomatic
- Clean up integration tests
- Remove non-virtual-dtor warnings by making classes final
- Update CMake configuration flag names
- Fix unused variable warning for release builds
- Introduce CI workflow based on GitHub Actions
v0.9.0
- Provide CMake config and PkgConfig files for tools
- Provide access to D-Bus message in high-level API
- Add API to set signal destination
- Add IProxy::getConnection() method
- Add README and sdbus-c++ tutorial as additional pages in doxydocs
- Enable default construction of PendingAsyncCall
- Add API to get message path and message destination
- Avoid propagating msg unpack exceptions to the event loop
- Fix issue #145: signals are not filtered by sender
- Fix race condition in Proxy and Object destructor
- Fix seg fault in Message::peekType()
- Add information to documentation about conan recipe
- Add cpack to build debian packages, split the packages by components
- Catch sdbus-c++ exceptions flying from Proxy callbacks to libsystemd
- Add createDefaultBusConnection() method
- Make resetting loop thread ID exception-safe
- Support Error parameter in signal handlers
- Add specific sections for tips and notes in the tutorial
- A few additional documentation and test updates and improvements
v1.0.0
- [[Breaking API change]] Fixed the API to send org.freedesktop.DBus.ObjectManager.InterfacesAdded and org.freedesktop.DBus.ObjectManager.InterfacesRemoved signals via the generated stubs layer.
- StandardInterfaces.h: Split ObjectManager_adaptor and ManagedObject_adaptor.
- New examples directory. First example covers the object manager. Further examples might follow.
v1.1.0
- Fix timeout handling for asynchronous method calls
- Add support for unregistering signal handler
- Add support for chrono literals in sdbus-c++-xml2cpp generator
- Additional little fixes and improvements in code, build system, and documentation
v1.2.0
- Add support for match rules
- Add support for session bus connection at custom address
- Add CMake variable for extra libsystemd config options
- Use pseudo D-Bus connection for plain messages
- Rename dont_request_slot tag to floating_slot
- Add validity checks for names and paths
- Remove executable flag from source files
- Detect missing type after array declaration
- Fix invalid assert on event fd
- Enable move for ObjectPath and Signature
- Add printer for std::chrono in googletest v1.11.0
- Fix potential undefined behavior in creation of sdbus::Error
- Additional little fixes and improvements in code, build system, and documentation

View File

@ -1,6 +1,10 @@
sdbus-c++ sdbus-c++
========= =========
![ci](https://github.com/Kistler-Group/sdbus-cpp/workflows/CI/badge.svg)
![license](https://img.shields.io/github/license/Kistler-Group/sdbus-cpp)
![release](https://img.shields.io/github/v/release/Kistler-Group/sdbus-cpp)
sdbus-c++ is a high-level C++ D-Bus library for Linux designed to provide expressive, easy-to-use API in modern C++. It adds another layer of abstraction on top of sd-bus, a nice, fresh C D-Bus implementation by systemd. sdbus-c++ is a high-level C++ D-Bus library for Linux designed to provide expressive, easy-to-use API in modern C++. It adds another layer of abstraction on top of sd-bus, a nice, fresh C D-Bus implementation by systemd.
sdbus-c++ has been written primarily as a replacement of dbus-c++, which currently suffers from a number of (unresolved) bugs, concurrency issues and inherent design complexities and limitations. sdbus-c++ has learned from dbus-c++ and has chosen a different path, a path of simple yet powerful design that is intuitive and friendly to the user and inherently free of those bugs. sdbus-c++ has been written primarily as a replacement of dbus-c++, which currently suffers from a number of (unresolved) bugs, concurrency issues and inherent design complexities and limitations. sdbus-c++ has learned from dbus-c++ and has chosen a different path, a path of simple yet powerful design that is intuitive and friendly to the user and inherently free of those bugs.
@ -16,8 +20,8 @@ The library is built using CMake:
$ mkdir build $ mkdir build
$ cd build $ cd build
$ cmake .. -DCMAKE_BUILD_TYPE=Release ${OTHER_CONFIG_FLAGS} $ cmake .. -DCMAKE_BUILD_TYPE=Release ${OTHER_CONFIG_FLAGS}
$ make $ cmake --build .
$ sudo make install $ sudo cmake --build . --target install
``` ```
### CMake configuration flags for sdbus-c++ ### CMake configuration flags for sdbus-c++
@ -32,17 +36,17 @@ $ sudo make install
* `BUILD_DOXYGEN_DOC` [boolean] * `BUILD_DOXYGEN_DOC` [boolean]
Option for building Doxygen documentation of sdbus-c++ API. If enabled, the documentation must still be built explicitly through `make doc`. Default value: `OFF`. Use `-DBUILD_DOXYGEN_DOC=OFF` to disable searching for Doxygen and building Doxygen documentation of sdbus-c++ API. Option for building Doxygen documentation of sdbus-c++ API. If enabled, the documentation must still be built explicitly through `cmake --build . --target doc`. Default value: `OFF`. Use `-DBUILD_DOXYGEN_DOC=OFF` to disable searching for Doxygen and building Doxygen documentation of sdbus-c++ API.
* `BUILD_TESTS` [boolean] * `BUILD_TESTS` [boolean]
Option for building sdbus-c++ unit and integration tests, invokable by `make test`. That incorporates downloading and building static libraries of Google Test. Default value: `OFF`. Use `-DBUILD_TESTS=ON` to enable building the tests. With this option turned on, you may also enable/disable the following options: Option for building sdbus-c++ unit and integration tests, invokable by `cmake --build . --target test` (Note: before invoking `cmake --build . --target test`, make sure you copy `tests/integrationtests/files/org.sdbuscpp.integrationtests.conf` file to `/etc/dbus-1/system.d` directory). That incorporates downloading and building static libraries of Google Test. Default value: `OFF`. Use `-DBUILD_TESTS=ON` to enable building the tests. With this option turned on, you may also enable/disable the following options:
* `BUILD_PERF_TESTS` [boolean] * `ENABLE_PERF_TESTS` [boolean]
Option for building sdbus-c++ performance tests. Default value: `OFF`. Option for building sdbus-c++ performance tests. Default value: `OFF`.
* `BUILD_STRESS_TESTS` [boolean] * `ENABLE_STRESS_TESTS` [boolean]
Option for building sdbus-c++ stress tests. Default value: `OFF`. Option for building sdbus-c++ stress tests. Default value: `OFF`.
@ -58,10 +62,22 @@ $ sudo make install
Defines version of systemd to be downloaded, built and integrated into sdbus-c++. Default value: `242`. Defines version of systemd to be downloaded, built and integrated into sdbus-c++. Default value: `242`.
* `LIBSYSTEMD_EXTRA_CONFIG_OPTS` [string]
Additional options to be passed as-is to the libsystemd build system (meson for systemd v242) in its configure step. Can be used for passing e.g. toolchain file path in case of cross builds. Default value: empty.
* `CMAKE_BUILD_TYPE` [string] * `CMAKE_BUILD_TYPE` [string]
This is a CMake-builtin option. Set to `Release` to build sdbus-c++ for production use. Set to `Debug` if you want to help further develop (and debug) the library :) This is a CMake-builtin option. Set to `Release` to build sdbus-c++ for production use. Set to `Debug` if you want to help further develop (and debug) the library :)
* `BUILD_SHARED_LIBS` [boolean]
This is a global CMake flag, promoted in sdbus-c++ project to a CMake option. Use this to control whether sdbus-c++ is built as either a shared or static library. Default value: `ON`.
* `BUILD_EXAMPLES` [boolean]
Build example programs which are located in the _example_ directory. Examples are not installed. Default value: `OFF`
Dependencies Dependencies
------------ ------------
@ -69,11 +85,12 @@ Dependencies
* `libsystemd` - systemd library containing sd-bus implementation. This library is part of systemd. Systemd at least v236 is needed. (In case you have a non-systemd environment, don't worry, see [Solving libsystemd dependency](docs/using-sdbus-c++.md#solving-libsystemd-dependency) for more information.) * `libsystemd` - systemd library containing sd-bus implementation. This library is part of systemd. Systemd at least v236 is needed. (In case you have a non-systemd environment, don't worry, see [Solving libsystemd dependency](docs/using-sdbus-c++.md#solving-libsystemd-dependency) for more information.)
* `googletest` - google unit testing framework, only necessary when building tests, will be downloaded and built automatically. * `googletest` - google unit testing framework, only necessary when building tests, will be downloaded and built automatically.
* `pkgconfig` - required for sdbus-c++ to be able to find some dependency packages. * `pkgconfig` - required for sdbus-c++ to be able to find some dependency packages.
* `expat` - necessary when building xml2cpp code generator (`BUILD_CODE_GEN` option is ON).
Licensing Licensing
--------- ---------
The library is distributed under LGPLv2.1 license. The library is distributed under LGPLv2.1 license, with a specific exception for macro/template/inline code in library header files.
References/documentation References/documentation
------------------------ ------------------------

View File

@ -1,10 +1,15 @@
find_program(MESON meson) find_program(MESON meson)
find_program(NINJA ninja) find_program(NINJA ninja)
find_program(GPERF gperf)
if((NOT MESON) OR (NOT NINJA)) if((NOT MESON) OR (NOT NINJA))
message(FATAL_ERROR "Meson and Ninja are required to build libsystemd") message(FATAL_ERROR "Meson and Ninja are required to build libsystemd")
endif() endif()
if(NOT GPERF)
message(WARNING "gperf was not found, libsystemd configuration may fail")
endif()
find_library(GLIBC_RT_LIBRARY rt) find_library(GLIBC_RT_LIBRARY rt)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(MOUNT mount) pkg_check_modules(MOUNT mount)
@ -14,6 +19,7 @@ if (NOT CAP_FOUND)
endif() endif()
set(LIBSYSTEMD_VERSION "242" CACHE STRING "libsystemd version (>=239) to build and incorporate into libsdbus-c++") set(LIBSYSTEMD_VERSION "242" CACHE STRING "libsystemd version (>=239) to build and incorporate into libsdbus-c++")
set(LIBSYSTEMD_EXTRA_CONFIG_OPTS "" CACHE STRING "Additional configuration options to be passed as-is to libsystemd build system")
if(NOT CMAKE_BUILD_TYPE) if(NOT CMAKE_BUILD_TYPE)
set(LIBSYSTEMD_BUILD_TYPE "plain") set(LIBSYSTEMD_BUILD_TYPE "plain")
@ -30,20 +36,23 @@ if(LIBSYSTEMD_VERSION GREATER "240")
set(BUILD_VERSION_H ${NINJA} -C <BINARY_DIR> version.h) set(BUILD_VERSION_H ${NINJA} -C <BINARY_DIR> version.h)
endif() endif()
message(STATUS "Building with embedded libsystemd v${LIBSYSTEMD_VERSION}")
include(ExternalProject) include(ExternalProject)
ExternalProject_Add(LibsystemdBuildProject ExternalProject_Add(LibsystemdBuildProject
PREFIX libsystemd-v${LIBSYSTEMD_VERSION} PREFIX libsystemd-v${LIBSYSTEMD_VERSION}
GIT_REPOSITORY https://github.com/systemd/systemd.git GIT_REPOSITORY https://github.com/systemd/systemd-stable.git
GIT_TAG v${LIBSYSTEMD_VERSION} GIT_TAG v${LIBSYSTEMD_VERSION}-stable
GIT_SHALLOW 1 GIT_SHALLOW 1
UPDATE_COMMAND "" UPDATE_COMMAND ""
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E remove <BINARY_DIR>/* CONFIGURE_COMMAND ${CMAKE_COMMAND} -E remove <BINARY_DIR>/*
COMMAND ${MESON} --prefix=<INSTALL_DIR> --buildtype=${LIBSYSTEMD_BUILD_TYPE} -Dstatic-libsystemd=pic <SOURCE_DIR> <BINARY_DIR> COMMAND ${MESON} --prefix=<INSTALL_DIR> --buildtype=${LIBSYSTEMD_BUILD_TYPE} -Dstatic-libsystemd=pic -Dselinux=false <SOURCE_DIR> <BINARY_DIR> ${LIBSYSTEMD_EXTRA_CONFIG_OPTS}
BUILD_COMMAND ${BUILD_VERSION_H} BUILD_COMMAND ${BUILD_VERSION_H}
COMMAND ${NINJA} -C <BINARY_DIR> libsystemd.a COMMAND ${NINJA} -C <BINARY_DIR> libsystemd.a
BUILD_ALWAYS 0 BUILD_ALWAYS 0
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory <SOURCE_DIR>/src/systemd <INSTALL_DIR>/include/systemd INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory <SOURCE_DIR>/src/systemd <INSTALL_DIR>/include/systemd
LOG_DOWNLOAD 1 LOG_UPDATE 1 LOG_CONFIGURE 1 LOG_BUILD 1) LOG_DOWNLOAD 1 LOG_UPDATE 1 LOG_CONFIGURE 1 LOG_BUILD 1
BUILD_BYPRODUCTS <BINARY_DIR>/libsystemd.a)
ExternalProject_Get_property(LibsystemdBuildProject SOURCE_DIR) ExternalProject_Get_property(LibsystemdBuildProject SOURCE_DIR)
ExternalProject_Get_property(LibsystemdBuildProject BINARY_DIR) ExternalProject_Get_property(LibsystemdBuildProject BINARY_DIR)

View File

@ -12,7 +12,11 @@ if(BUILD_DOXYGEN_DOC)
COMMENT "Generating API documentation with Doxygen" COMMENT "Generating API documentation with Doxygen"
VERBATIM) VERBATIM)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_DOCDIR} OPTIONAL) # workaround bug https://github.com/doxygen/doxygen/pull/6787
add_custom_command(TARGET doc POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/sdbus-c++-class-diagram.png ${CMAKE_CURRENT_BINARY_DIR}/html/.)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_DOCDIR} OPTIONAL COMPONENT doc)
else() else()
message(WARNING "Documentation enabled, but Doxygen cannot be found") message(WARNING "Documentation enabled, but Doxygen cannot be found")
endif() endif()
@ -22,4 +26,4 @@ install(FILES sdbus-c++-class-diagram.png
sdbus-c++-class-diagram.uml sdbus-c++-class-diagram.uml
systemd-dbus-config.md systemd-dbus-config.md
using-sdbus-c++.md using-sdbus-c++.md
DESTINATION ${CMAKE_INSTALL_DOCDIR}) DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT doc)

View File

@ -790,7 +790,9 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched. # Note: If this tag is empty the current directory is searched.
INPUT = "@CMAKE_CURRENT_SOURCE_DIR@/../include" INPUT = "@CMAKE_CURRENT_SOURCE_DIR@/../include" \
@CMAKE_CURRENT_SOURCE_DIR@/../README.md \
@CMAKE_CURRENT_SOURCE_DIR@/using-sdbus-c++.md
# This tag can be used to specify the character encoding of the source files # This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@ -982,7 +984,7 @@ FILTER_SOURCE_PATTERNS =
# (index.html). This can be useful if you have a project on for instance GitHub # (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output. # and want to reuse the introduction page also for the doxygen output.
USE_MDFILE_AS_MAINPAGE = USE_MDFILE_AS_MAINPAGE = README.md
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to source browsing # Configuration options related to source browsing

View File

@ -1,4 +1,4 @@
Systemd and dbus configuration Systemd and D-Bus configuration
======================= =======================
**Table of contents** **Table of contents**
@ -10,15 +10,13 @@ Systemd and dbus configuration
Introduction Introduction
------------ ------------
To run executable as a systemd service you may need some additional setup. For example, you may need explicitly allow To run executable as a systemd service you may need some additional setup. For example, you may need explicitly allow the usage of your service. Following chapters contain template configurations.
the usage of your service. Following chapters contain template configurations.
Systemd configuration Systemd configuration
--------------------------------------- ---------------------------------------
Filename should use `.service` extension. It also must be placed in configuration directory (/etc/systemd/system in Filename should use `.service` extension. It also must be placed in configuration directory (/etc/systemd/system in Ubuntu 18.04.1 LTS)
Ubuntu 18.04.1 LTS)
``` ```
[Unit] [Unit]
@ -31,12 +29,10 @@ ExecStart=/path/to/executable
WantedBy=multi-user.target WantedBy=multi-user.target
``` ```
Dbus configuration D-Bus configuration
------------------ ------------------
Typical default D-Bus configuration does not allow to register services except explicitly allowed. Filename should Typical default D-Bus configuration does not allow to register services except explicitly allowed. To allow a service to register its D-Bus API, we must place an appropriate conf file in `/etc/dbus-1/system.d/` directory. The conf file name must be `<service-name>.conf`. I.e., full file path for Concatenator example from sdbus-c++ tutorial would be `/etc/dbus-1/system.d/org.sdbuscpp.concatenator.conf`. And here is template configuration to use its D-Bus interface under root:
contain name of your service, e.g `/etc/dbus-1/system.d/org.sdbuscpp.concatenator.conf`. So, here is template
configuration to use dbus interface under root:
``` ```
<!DOCTYPE busconfig PUBLIC <!DOCTYPE busconfig PUBLIC
@ -45,10 +41,10 @@ configuration to use dbus interface under root:
<busconfig> <busconfig>
<policy user="root"> <policy user="root">
<allow own="org.sdbuscpp.concatenator"/> <allow own="org.sdbuscpp.concatenator"/>
<allow send_destination="org.sdbuscpp"/> <allow send_destination="org.sdbuscpp.concatenator"/>
<allow send_interface="org.sdbuscpp.concatenator"/> <allow send_interface="org.sdbuscpp.concatenator"/>
</policy> </policy>
</busconfig> </busconfig>
``` ```
If you need access from other user `root` should be substituted by desired username. For more refer to `man dbus-daemon`. If you need access from other user then `root` should be substituted by desired username. Or you can simply use policy `<policy context="default">` like [conf file](/tests/integrationtests/files/org.sdbuscpp.integrationtests.conf) for sdbus-c++ integration tests is doing it. For more information refer to `man dbus-daemon`.

View File

@ -19,7 +19,8 @@ Using sdbus-c++ library
14. [Asynchronous client-side methods](#asynchronous-client-side-methods) 14. [Asynchronous client-side methods](#asynchronous-client-side-methods)
15. [Using D-Bus properties](#using-d-bus-properties) 15. [Using D-Bus properties](#using-d-bus-properties)
16. [Standard D-Bus interfaces](#standard-d-bus-interfaces) 16. [Standard D-Bus interfaces](#standard-d-bus-interfaces)
17. [Conclusion](#conclusion) 17. [Support for match rules](#support-for-match-rules)
18. [Conclusion](#conclusion)
Introduction Introduction
------------ ------------
@ -51,7 +52,23 @@ PKG_CHECK_MODULES(SDBUSCPP, [sdbus-c++ >= 0.6],,
) )
``` ```
Note: sdbus-c++ library uses a number of modern C++17 features. Please make certain you have a recent compiler (gcc >= 7, clang >= 6). > **_Note_:** sdbus-c++ library uses a number of modern C++17 features. Please make certain you have a recent compiler (gcc >= 7, clang >= 6).
If you intend to use stub generator (explained later) in your project to generate interface headers from XML, you can integrate that too with CMake or `pkg-config`:
```cmake
# First, find sdbus-c++-tools
find_package(sdbus-c++-tools REQUIRED)
# Use the sdbus-c++-xml2cpp in SDBusCpp namespace to generate the headers
add_custom_command(
OUTPUT myproject-client-glue.h myproject-server-glue.h
COMMAND SDBusCpp::sdbus-c++-xml2cpp ${PROJECT_SOURCE_DIR}/dbus/myproject-bindings.xml
--proxy=myproject-client-glue.h --adaptor=myproject-server-glue.h
DEPENDS dbus/myproject-bindings.xml
COMMENT "Generating D-Bus interfaces for ${PROJECT_NAME}."
)
```
Solving libsystemd dependency Solving libsystemd dependency
----------------------------- -----------------------------
@ -82,7 +99,7 @@ $ ninja libsystemd.so.0.26.0 # or another version number depending which system
### Building and distributing libsystemd as part of sdbus-c++ ### Building and distributing libsystemd as part of sdbus-c++
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. 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, among others, `meson`, `ninja`, `git`, `gperf`, and -- primarily -- libraries and library headers for `libmount`, `libcap` and `librt` (part of glibc). Be sure to check out the systemd documentation for the 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). 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).
@ -96,10 +113,30 @@ There are Yocto recipes for sdbus-c++ available in the [`meta-oe`](https://githu
* 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 links it into the sdbus-c++ shared library. The recipe also supports ptest. * 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 links it into the 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. * 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. > **_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.
### Conan
sdbus-c++ recipe is available in ConanCenter repository as [`sdbus-cpp`](https://conan.io/center/sdbus-cpp).
### Buildroot
There is the Buildroot package [`sdbus-cpp`](https://git.buildroot.net/buildroot/tree/package/sdbus-cpp?h=2022.02) to build sdbus-c++ library itself without a code generation tool.
Contributors willing to help with bringing sdbus-c++ to other popular package systems are welcome. Contributors willing to help with bringing sdbus-c++ to other popular package systems are welcome.
Verifying sdbus-c++
-------------------
You can build and run sdbus-c++ unit and integration tests to verify sdbus-c++ build:
```
$ cd build
$ cmake .. -DBUILD_TESTS=ON
$ sudo cp ../tests/integrationtests/files/org.sdbuscpp.integrationtests.conf /etc/dbus-1/system.d/
$ cmake --build . --target test
```
Header files and namespaces Header files and namespaces
--------------------------- ---------------------------
@ -189,6 +226,8 @@ Let's have an object `/org/sdbuscpp/concatenator` that implements the `org.sdbus
In the following sections, we will elaborate on the ways of implementing such an object on both the server and the client side. In the following sections, we will elaborate on the ways of implementing such an object on both the server and the client side.
> **Before running Concatenator example in your system:** In order for your service to be allowed to provide a D-Bus API on system bus, a D-Bus security policy file has to be put in place for that service. Otherwise the service will fail to start (you'll get `[org.freedesktop.DBus.Error.AccessDenied] Failed to request bus name (Permission denied)`, for example). To make the Concatenator example work in your system, [look in this section of systemd configuration](systemd-dbus-config.md#dbus-configuration) for how to name the file, where to place it, how to populate it. For further information, consult [dbus-daemon documentation](https://dbus.freedesktop.org/doc/dbus-daemon.1.html), sections *INTEGRATING SYSTEM SERVICES* and *CONFIGURATION FILE*. As an example used for sdbus-c++ integration tests, you may look at the [policy file for sdbus-c++ integration tests](/tests/integrationtests/files/org.sdbuscpp.integrationtests.conf).
Implementing the Concatenator example using basic sdbus-c++ API layer Implementing the Concatenator example using basic sdbus-c++ API layer
--------------------------------------------------------------------- ---------------------------------------------------------------------
@ -207,7 +246,7 @@ This is how a simple Concatenator service implemented upon the basic sdbus-c++ A
#include <vector> #include <vector>
#include <string> #include <string>
// Yeah, global variable is ugly, but this is just an example and we want to access // Yeah, global variable is ugly, but this is just an example and we want to access
// the concatenator instance from within the concatenate method handler to be able // the concatenator instance from within the concatenate method handler to be able
// to emit signals. // to emit signals.
sdbus::IObject* g_concatenator{}; sdbus::IObject* g_concatenator{};
@ -217,26 +256,26 @@ void concatenate(sdbus::MethodCall call)
// Deserialize the collection of numbers from the message // Deserialize the collection of numbers from the message
std::vector<int> numbers; std::vector<int> numbers;
call >> numbers; call >> numbers;
// Deserialize separator from the message // Deserialize separator from the message
std::string separator; std::string separator;
call >> separator; call >> separator;
// Return error if there are no numbers in the collection // Return error if there are no numbers in the collection
if (numbers.empty()) if (numbers.empty())
throw sdbus::Error("org.sdbuscpp.Concatenator.Error", "No numbers provided"); throw sdbus::Error("org.sdbuscpp.Concatenator.Error", "No numbers provided");
std::string result; std::string result;
for (auto number : numbers) for (auto number : numbers)
{ {
result += (result.empty() ? std::string() : separator) + std::to_string(number); result += (result.empty() ? std::string() : separator) + std::to_string(number);
} }
// Serialize resulting string to the reply and send the reply to the caller // Serialize resulting string to the reply and send the reply to the caller
auto reply = call.createReply(); auto reply = call.createReply();
reply << result; reply << result;
reply.send(); reply.send();
// Emit 'concatenated' signal // Emit 'concatenated' signal
const char* interfaceName = "org.sdbuscpp.Concatenator"; const char* interfaceName = "org.sdbuscpp.Concatenator";
auto signal = g_concatenator->createSignal(interfaceName, "concatenated"); auto signal = g_concatenator->createSignal(interfaceName, "concatenated");
@ -253,9 +292,9 @@ int main(int argc, char *argv[])
// Create concatenator D-Bus object. // Create concatenator D-Bus object.
const char* objectPath = "/org/sdbuscpp/concatenator"; const char* objectPath = "/org/sdbuscpp/concatenator";
auto concatenator = sdbus::createObject(*connection, objectPath); auto concatenator = sdbus::createObject(*connection, objectPath);
g_concatenator = concatenator.get(); g_concatenator = concatenator.get();
// Register D-Bus methods and signals on the concatenator object, and exports the object. // Register D-Bus methods and signals on the concatenator object, and exports the object.
const char* interfaceName = "org.sdbuscpp.Concatenator"; const char* interfaceName = "org.sdbuscpp.Concatenator";
concatenator->registerMethod(interfaceName, "concatenate", "ais", "s", &concatenate); concatenator->registerMethod(interfaceName, "concatenate", "ais", "s", &concatenate);
@ -286,7 +325,7 @@ void onConcatenated(sdbus::Signal& signal)
{ {
std::string concatenatedString; std::string concatenatedString;
signal >> concatenatedString; signal >> concatenatedString;
std::cout << "Received signal with concatenated string " << concatenatedString << std::endl; std::cout << "Received signal with concatenated string " << concatenatedString << std::endl;
} }
@ -298,15 +337,15 @@ int main(int argc, char *argv[])
const char* destinationName = "org.sdbuscpp.concatenator"; const char* destinationName = "org.sdbuscpp.concatenator";
const char* objectPath = "/org/sdbuscpp/concatenator"; const char* objectPath = "/org/sdbuscpp/concatenator";
auto concatenatorProxy = sdbus::createProxy(destinationName, objectPath); auto concatenatorProxy = sdbus::createProxy(destinationName, objectPath);
// Let's subscribe for the 'concatenated' signals // Let's subscribe for the 'concatenated' signals
const char* interfaceName = "org.sdbuscpp.Concatenator"; const char* interfaceName = "org.sdbuscpp.Concatenator";
concatenatorProxy->registerSignalHandler(interfaceName, "concatenated", &onConcatenated); concatenatorProxy->registerSignalHandler(interfaceName, "concatenated", &onConcatenated);
concatenatorProxy->finishRegistration(); concatenatorProxy->finishRegistration();
std::vector<int> numbers = {1, 2, 3}; std::vector<int> numbers = {1, 2, 3};
std::string separator = ":"; std::string separator = ":";
// Invoke concatenate on given interface of the object // Invoke concatenate on given interface of the object
{ {
auto method = concatenatorProxy->createMethodCall(interfaceName, "concatenate"); auto method = concatenatorProxy->createMethodCall(interfaceName, "concatenate");
@ -316,7 +355,7 @@ int main(int argc, char *argv[])
reply >> result; reply >> result;
assert(result == "1:2:3"); assert(result == "1:2:3");
} }
// Invoke concatenate again, this time with no numbers and we shall get an error // Invoke concatenate again, this time with no numbers and we shall get an error
{ {
auto method = concatenatorProxy->createMethodCall(interfaceName, "concatenate"); auto method = concatenatorProxy->createMethodCall(interfaceName, "concatenate");
@ -331,10 +370,10 @@ int main(int argc, char *argv[])
std::cerr << "Got concatenate error " << e.getName() << " with message " << e.getMessage() << std::endl; std::cerr << "Got concatenate error " << e.getName() << " with message " << e.getMessage() << std::endl;
} }
} }
// Give sufficient time to receive 'concatenated' signal from the first concatenate invocation // Give sufficient time to receive 'concatenated' signal from the first concatenate invocation
sleep(1); sleep(1);
return 0; return 0;
} }
``` ```
@ -378,9 +417,9 @@ Of course, at any time before or after running the event loop on the connection,
On the **client** side we have more options when creating D-Bus proxies. That corresponds to three overloads of the `createProxy()` factory: On the **client** side we have more options when creating D-Bus proxies. That corresponds to three overloads of the `createProxy()` factory:
* In case we (the application) already maintain a D-Bus connection, e.g. because we a D-Bus service anyway, the simple and typical approach is to create proxy upon that connection. The proxy will share the connection with others. So we pass connection reference to proxy factory. With this approach we must of course ensure that the connection exists as long as the proxy exists. * In case we (the application) already maintain a D-Bus connection, e.g. because we a D-Bus service anyway, the simple and typical approach is to create proxy upon that connection. The proxy will share the connection with others. So we pass connection reference to proxy factory. With this approach we must of course ensure that the connection exists as long as the proxy exists.
* Or -- and this is typical when we have a simple D-Bus client application -- we have another option: we let proxy maintain its own connection (and an associated thread): * Or -- and this is typical when we have a simple D-Bus client application -- we have another option: we let proxy maintain its own connection (and an associated thread):
* We either create the connection ourselves and `std::move` it to the proxy object factory. The proxy becomes an owner of this connection, and will run the event loop on that connection. This had the advantage that we may choose the type of connection (system, session, remote). * We either create the connection ourselves and `std::move` it to the proxy object factory. The proxy becomes an owner of this connection, and will run the event loop on that connection. This had the advantage that we may choose the type of connection (system, session, remote).
* Or we don't bother about any connection at all when creating a proxy (the factory overload with no connection parameter). Under the hood, the proxy creates its own *system bus* connection, creates a separate thread and runs an event loop in it. This is **the simplest approach** for non-complex D-Bus clients. For more complex ones, with big number of proxies, this hurts scalability but may improve concurrency (see discussion higher above), so we should make a conscious choice. * Or we don't bother about any connection at all when creating a proxy (the factory overload with no connection parameter). Under the hood, the proxy creates its own *system bus* connection, creates a separate thread and runs an event loop in it. This is **the simplest approach** for non-complex D-Bus clients. For more complex ones, with big number of proxies, this hurts scalability but may improve concurrency (see discussion higher above), so we should make a conscious choice.
@ -403,7 +442,7 @@ Thus, in the end of the day, the code written using the convenience API is:
- significantly shorter, - significantly shorter,
- almost as fast as one written using the basic API layer. - almost as fast as one written using the basic API layer.
The code written using this layer expresses in a declarative way *what* it does, rather then *how*. Let's look at code samples. The code written using this layer expresses in a declarative way *what* it does, rather than *how*. Let's look at code samples.
### Server side ### Server side
@ -412,30 +451,6 @@ The code written using this layer expresses in a declarative way *what* it does,
#include <vector> #include <vector>
#include <string> #include <string>
// Yeah, global variable is ugly, but this is just an example and we want to access
// the concatenator instance from within the concatenate method handler to be able
// to emit signals.
sdbus::IObject* g_concatenator{};
std::string concatenate(const std::vector<int> numbers, const std::string& separator)
{
// Return error if there are no numbers in the collection
if (numbers.empty())
throw sdbus::Error("org.sdbuscpp.Concatenator.Error", "No numbers provided");
std::string result;
for (auto number : numbers)
{
result += (result.empty() ? std::string() : separator) + std::to_string(number);
}
// Emit 'concatenated' signal
const char* interfaceName = "org.sdbuscpp.Concatenator";
g_concatenator->emitSignal("concatenated").onInterface(interfaceName).withArguments(result);
return result;
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
// Create D-Bus connection to the system bus and requests name on it. // Create D-Bus connection to the system bus and requests name on it.
@ -446,11 +461,28 @@ int main(int argc, char *argv[])
const char* objectPath = "/org/sdbuscpp/concatenator"; const char* objectPath = "/org/sdbuscpp/concatenator";
auto concatenator = sdbus::createObject(*connection, objectPath); auto concatenator = sdbus::createObject(*connection, objectPath);
g_concatenator = concatenator.get(); auto concatenate = [&concatenator](const std::vector<int> numbers, const std::string& separator)
{
// Return error if there are no numbers in the collection
if (numbers.empty())
throw sdbus::Error("org.sdbuscpp.Concatenator.Error", "No numbers provided");
std::string result;
for (auto number : numbers)
{
result += (result.empty() ? std::string() : separator) + std::to_string(number);
}
// Emit 'concatenated' signal
const char* interfaceName = "org.sdbuscpp.Concatenator";
concatenator->emitSignal("concatenated").onInterface(interfaceName).withArguments(result);
return result;
};
// Register D-Bus methods and signals on the concatenator object, and exports the object. // Register D-Bus methods and signals on the concatenator object, and exports the object.
const char* interfaceName = "org.sdbuscpp.Concatenator"; const char* interfaceName = "org.sdbuscpp.Concatenator";
concatenator->registerMethod("concatenate").onInterface(interfaceName).implementedAs(&concatenate); concatenator->registerMethod("concatenate").onInterface(interfaceName).implementedAs(std::move(concatenate));
concatenator->registerSignal("concatenated").onInterface(interfaceName).withParameters<std::string>(); concatenator->registerSignal("concatenated").onInterface(interfaceName).withParameters<std::string>();
concatenator->finishRegistration(); concatenator->finishRegistration();
@ -479,22 +511,22 @@ int main(int argc, char *argv[])
const char* destinationName = "org.sdbuscpp.concatenator"; const char* destinationName = "org.sdbuscpp.concatenator";
const char* objectPath = "/org/sdbuscpp/concatenator"; const char* objectPath = "/org/sdbuscpp/concatenator";
auto concatenatorProxy = sdbus::createProxy(destinationName, objectPath); auto concatenatorProxy = sdbus::createProxy(destinationName, objectPath);
// Let's subscribe for the 'concatenated' signals // Let's subscribe for the 'concatenated' signals
const char* interfaceName = "org.sdbuscpp.Concatenator"; const char* interfaceName = "org.sdbuscpp.Concatenator";
concatenatorProxy->uponSignal("concatenated").onInterface(interfaceName).call([](const std::string& str){ onConcatenated(str); }); concatenatorProxy->uponSignal("concatenated").onInterface(interfaceName).call([](const std::string& str){ onConcatenated(str); });
concatenatorProxy->finishRegistration(); concatenatorProxy->finishRegistration();
std::vector<int> numbers = {1, 2, 3}; std::vector<int> numbers = {1, 2, 3};
std::string separator = ":"; std::string separator = ":";
// Invoke concatenate on given interface of the object // Invoke concatenate on given interface of the object
{ {
std::string concatenatedString; std::string concatenatedString;
concatenatorProxy->callMethod("concatenate").onInterface(interfaceName).withArguments(numbers, separator).storeResultsTo(concatenatedString); concatenatorProxy->callMethod("concatenate").onInterface(interfaceName).withArguments(numbers, separator).storeResultsTo(concatenatedString);
assert(concatenatedString == "1:2:3"); assert(concatenatedString == "1:2:3");
} }
// Invoke concatenate again, this time with no numbers and we shall get an error // Invoke concatenate again, this time with no numbers and we shall get an error
{ {
try try
@ -507,10 +539,10 @@ int main(int argc, char *argv[])
std::cerr << "Got concatenate error " << e.getName() << " with message " << e.getMessage() << std::endl; std::cerr << "Got concatenate error " << e.getName() << " with message " << e.getMessage() << std::endl;
} }
} }
// Give sufficient time to receive 'concatenated' signal from the first concatenate invocation // Give sufficient time to receive 'concatenated' signal from the first concatenate invocation
sleep(1); sleep(1);
return 0; return 0;
} }
``` ```
@ -519,11 +551,49 @@ When registering methods, calling methods or emitting signals, multiple lines of
sdbus-c++ users shall prefer the convenience API to the lower level, basic API. When feasible, using generated adaptor and proxy stubs is even better. These stubs provide yet another, higher API level built on top of the convenience API. They are described in the following section. sdbus-c++ users shall prefer the convenience API to the lower level, basic API. When feasible, using generated adaptor and proxy stubs is even better. These stubs provide yet another, higher API level built on top of the convenience API. They are described in the following section.
Tip: When registering a D-Bus object, we can additionally provide names of input and output parameters of its methods and names of parameters of its signals. When the object is introspected, these names are listed in the resulting introspection XML, which improves the description of object's interfaces: > **_Note_:** By default, signal callback handlers are not invoked (i.e., the signal is silently dropped) if there is a signal signature mismatch. If clients want to be informed of such situations, they can prepend `const sdbus::Error*` parameter to their signal callback handler's parameter list. This argument will be `nullptr` in normal cases, and will provide access to the corresponding `sdbus::Error` object in case of deserialization failures. An example of a handler with the signature (`int`) different from the real signal contents (`string`):
> ```c++
> void onConcatenated(const sdbus::Error* e, int wrongParameter)
> {
> assert(e);
> assert(e->getMessage() == "Failed to deserialize a int32 value");
> }
> ```
> **_Tip_:** When registering a D-Bus object, we can additionally provide names of input and output parameters of its methods and names of parameters of its signals. When the object is introspected, these names are listed in the resulting introspection XML, which improves the description of object's interfaces:
> ```c++
> concatenator->registerMethod("concatenate")
> .onInterface(interfaceName)
> .withInputParamNames("numbers", "separator")
> .withOutputParamNames("concatenatedString")
> .implementedAs(&concatenate);
> concatenator->registerSignal("concatenated")
> .onInterface(interfaceName)
> .withParameters<std::string>("concatenatedString");
> ```
### Accessing a corresponding D-Bus message
The convenience API hides away the level of D-Bus messages. But the messages carry with them additional information that may need in some implementations. For example, a name of a method call sender; or info on credentials. Is there a way to access a corresponding D-Bus message in a high-level callback handler?
Yes, there is -- we can access the corresponding D-Bus message in:
* method implementation callback handlers (server side),
* property set implementation callback handlers (server side),
* signal callback handlers (client side).
Both `IObject` and `IProxy` provide the `getCurrentlyProcessedMessage()` method. This method is meant to be called from within a callback handler. It returns a pointer to the corresponding D-Bus message that caused invocation of the handler. The pointer is only valid (dereferencable) as long as the flow of execution does not leave the callback handler. When called from other contexts/threads, the pointer may be both zero or non-zero, and its dereferencing is undefined behavior.
An excerpt of the above example of concatenator modified to print out a name of the sender of method call:
```c++ ```c++
concatenator->registerMethod("concatenate").onInterface(interfaceName).withInputParamNames("numbers", "separator").withOutputParamNames("concatenatedString").implementedAs(&concatenate); auto concatenate = [&concatenator](const std::vector<int> numbers, const std::string& separator)
concatenator->registerSignal("concatenated").onInterface(interfaceName).withParameters<std::string>("concatenatedString"); {
const auto* methodCallMsg = concatenator->getCurrentlyProcessedMessage();
std::cout << "Sender of this method call: " << methodCallMsg.getSender() << std::endl;
/*...*/
};
``` ```
Implementing the Concatenator example using sdbus-c++-generated stubs Implementing the Concatenator example using sdbus-c++-generated stubs
@ -688,7 +758,7 @@ class Concatenator : public sdbus::AdaptorInterfaces<org::sdbuscpp::Concatenator
{ {
public: public:
Concatenator(sdbus::IConnection& connection, std::string objectPath) Concatenator(sdbus::IConnection& connection, std::string objectPath)
: sdbus::AdaptorInterfaces(connection, std::move(objectPath)) : AdaptorInterfaces(connection, std::move(objectPath))
{ {
registerAdaptor(); registerAdaptor();
} }
@ -704,23 +774,25 @@ protected:
// Return error if there are no numbers in the collection // Return error if there are no numbers in the collection
if (numbers.empty()) if (numbers.empty())
throw sdbus::Error("org.sdbuscpp.Concatenator.Error", "No numbers provided"); throw sdbus::Error("org.sdbuscpp.Concatenator.Error", "No numbers provided");
// Concatenate the numbers // Concatenate the numbers
std::string result; std::string result;
for (auto number : numbers) for (auto number : numbers)
{ {
result += (result.empty() ? std::string() : separator) + std::to_string(number); result += (result.empty() ? std::string() : separator) + std::to_string(number);
} }
// Emit the 'concatenated' signal with the resulting string // Emit the 'concatenated' signal with the resulting string
emitConcatenated(result); emitConcatenated(result);
// Return the resulting string // Return the resulting string
return result; return result;
} }
}; };
``` ```
> **_Tip_:** By inheriting from `sdbus::AdaptorInterfaces`, we get access to the protected `getObject()` method. We can call this method inside our adaptor implementation class to access the underlying `IObject` object.
That's it. We now have an implementation of a D-Bus object implementing `org.sdbuscpp.Concatenator` interface. Let's now create a service publishing the object. That's it. We now have an implementation of a D-Bus object implementing `org.sdbuscpp.Concatenator` interface. Let's now create a service publishing the object.
```cpp ```cpp
@ -765,12 +837,12 @@ class ConcatenatorProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::Concatena
{ {
public: public:
ConcatenatorProxy(std::string destination, std::string objectPath) ConcatenatorProxy(std::string destination, std::string objectPath)
: sdbus::ProxyInterfaces(std::move(destination), std::move(objectPath)) : ProxyInterfaces(std::move(destination), std::move(objectPath))
{ {
registerProxy(); registerProxy();
} }
~Concatenator() ~ConcatenatorProxy()
{ {
unregisterProxy(); unregisterProxy();
} }
@ -783,6 +855,8 @@ protected:
}; };
``` ```
> **_Tip_:** By inheriting from `sdbus::ProxyInterfaces`, we get access to the protected `getProxy()` method. We can call this method inside our proxy implementation class to access the underlying `IProxy` object.
In the above example, a proxy is created that creates and maintains its own system bus connection. However, there are `ProxyInterfaces` class template constructor overloads that also take the connection from the user as the first parameter, and pass that connection over to the underlying proxy. The connection instance is used by all interfaces listed in the `ProxyInterfaces` template parameter list. In the above example, a proxy is created that creates and maintains its own system bus connection. However, there are `ProxyInterfaces` class template constructor overloads that also take the connection from the user as the first parameter, and pass that connection over to the underlying proxy. The connection instance is used by all interfaces listed in the `ProxyInterfaces` template parameter list.
Note however that there are multiple `ProxyInterfaces` constructor overloads, and they differ in how the proxy behaves towards the D-Bus connection. These overloads precisely map the `sdbus::createProxy` overloads, as they are actually implemented on top of them. See [Proxy and D-Bus connection](#Proxy-and-D-Bus-connection) for more info. We can even create a `IProxy` instance on our own, and inject it into our proxy class -- there is a constructor overload for it in `ProxyInterfaces`. This can help if we need to provide mocked implementations in our unit tests. Note however that there are multiple `ProxyInterfaces` constructor overloads, and they differ in how the proxy behaves towards the D-Bus connection. These overloads precisely map the `sdbus::createProxy` overloads, as they are actually implemented on top of them. See [Proxy and D-Bus connection](#Proxy-and-D-Bus-connection) for more info. We can even create a `IProxy` instance on our own, and inject it into our proxy class -- there is a constructor overload for it in `ProxyInterfaces`. This can help if we need to provide mocked implementations in our unit tests.
@ -802,11 +876,11 @@ int main(int argc, char *argv[])
std::vector<int> numbers = {1, 2, 3}; std::vector<int> numbers = {1, 2, 3};
std::string separator = ":"; std::string separator = ":";
// Invoke concatenate with some numbers // Invoke concatenate with some numbers
auto concatenatedString = concatenatorProxy.concatenate(numbers, separator); auto concatenatedString = concatenatorProxy.concatenate(numbers, separator);
assert(concatenatedString == "1:2:3"); assert(concatenatedString == "1:2:3");
// Invoke concatenate again, this time with no numbers and we shall get an error // Invoke concatenate again, this time with no numbers and we shall get an error
try try
{ {
@ -817,14 +891,35 @@ int main(int argc, char *argv[])
{ {
std::cerr << "Got concatenate error " << e.getName() << " with message " << e.getMessage() << std::endl; std::cerr << "Got concatenate error " << e.getName() << " with message " << e.getMessage() << std::endl;
} }
// Give sufficient time to receive 'concatenated' signal from the first concatenate invocation // Give sufficient time to receive 'concatenated' signal from the first concatenate invocation
sleep(1); sleep(1);
return 0; return 0;
} }
``` ```
### Accessing a corresponding D-Bus message
Simply combine `getObject()`/`getProxy()` and `getCurrentlyProcessedMessage()` methods. Both were already discussed above. An example:
```c++
class Concatenator : public sdbus::AdaptorInterfaces</*...*/>
{
public:
/*...*/
protected:
std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator) override
{
const auto* methodCallMsg = getObject().getCurrentlyProcessedMessage();
std::cout << "Sender of this method call: " << methodCallMsg.getSender() << std::endl;
/*...*/
}
};
```
Asynchronous server-side methods Asynchronous server-side methods
-------------------------------- --------------------------------
@ -840,11 +935,11 @@ void concatenate(sdbus::MethodCall call)
// Deserialize the collection of numbers from the message // Deserialize the collection of numbers from the message
std::vector<int> numbers; std::vector<int> numbers;
call >> numbers; call >> numbers;
// Deserialize separator from the message // Deserialize separator from the message
std::string separator; std::string separator;
call >> separator; call >> separator;
// Launch a thread for async execution... // Launch a thread for async execution...
std::thread([numbers = std::move(numbers), separator = std::move(separator), call = std::move(call)]() std::thread([numbers = std::move(numbers), separator = std::move(separator), call = std::move(call)]()
{ {
@ -856,18 +951,18 @@ void concatenate(sdbus::MethodCall call)
reply.send(); reply.send();
return; return;
} }
std::string result; std::string result;
for (auto number : numbers) for (auto number : numbers)
{ {
result += (result.empty() ? std::string() : separator) + std::to_string(number); result += (result.empty() ? std::string() : separator) + std::to_string(number);
} }
// Let's send the reply message back to the client // Let's send the reply message back to the client
auto reply = call.createReply(); auto reply = call.createReply();
reply << result; reply << result;
reply.send(); reply.send();
// Emit 'concatenated' signal (creating and emitting signals is thread-safe) // Emit 'concatenated' signal (creating and emitting signals is thread-safe)
const char* interfaceName = "org.sdbuscpp.Concatenator"; const char* interfaceName = "org.sdbuscpp.Concatenator";
auto signal = g_concatenator->createSignal(interfaceName, "concatenated"); auto signal = g_concatenator->createSignal(interfaceName, "concatenated");
@ -905,29 +1000,29 @@ void concatenate(sdbus::Result<std::string>&& result, std::vector<int32_t> numbe
methodResult.returnError({"org.sdbuscpp.Concatenator.Error", "No numbers provided"}); methodResult.returnError({"org.sdbuscpp.Concatenator.Error", "No numbers provided"});
return; return;
} }
std::string result; std::string result;
for (auto number : numbers) for (auto number : numbers)
{ {
result += (result.empty() ? std::string() : separator) + std::to_string(number); result += (result.empty() ? std::string() : separator) + std::to_string(number);
} }
// Let's send the reply message back to the client // Let's send the reply message back to the client
methodResult.returnReply(result); methodResult.returnResults(result);
// Emit the 'concatenated' signal with the resulting string // Emit the 'concatenated' signal with the resulting string
this->emitConcatenated(result); this->emitConcatenated(result);
}).detach(); }).detach();
} }
``` ```
The `Result` is a convenience class that represents a future method result, and it is where we write the results (`returnReply()`) or an error (`returnError()`) which we want to send back to the client. The `Result` is a convenience class that represents a future method result, and it is where we write the results (`returnResults()`) or an error (`returnError()`) which we want to send back to the client.
Registraion (`implementedAs()`) doesn't change. Nothing else needs to change. Registraion (`implementedAs()`) doesn't change. Nothing else needs to change.
### Marking server-side async methods in the IDL ### Marking server-side async methods in the IDL
sdbus-c++ stub generator can generate stub code for server-side async methods. We just need to annotate the method with the `annotate` element having the "org.freedesktop.DBus.Method.Async" name. The element value must be either "server" (async method on server-side only) or "clientserver" (async method on both client- and server-side): sdbus-c++ stub generator can generate stub code for server-side async methods. We just need to annotate the method with `org.freedesktop.DBus.Method.Async`. The annotation element value must be either `server` (async method on server-side only) or `clientserver` (async method on both client- and server-side):
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -962,7 +1057,7 @@ Considering the Concatenator example based on lower-level API, if we wanted to c
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
/* ... */ /* ... */
auto callback = [](MethodReply& reply, const sdbus::Error* error) auto callback = [](MethodReply& reply, const sdbus::Error* error)
{ {
if (error == nullptr) // No error if (error == nullptr) // No error
@ -984,7 +1079,7 @@ int main(int argc, char *argv[])
concatenatorProxy->callMethod(method, callback); concatenatorProxy->callMethod(method, callback);
// When the reply comes, we shall get "Got concatenate result 1:2:3" on the standard output // When the reply comes, we shall get "Got concatenate result 1:2:3" on the standard output
} }
// Invoke concatenate again, this time with no numbers and we shall get an error // Invoke concatenate again, this time with no numbers and we shall get an error
{ {
auto method = concatenatorProxy->createMethodCall(interfaceName, "concatenate"); auto method = concatenatorProxy->createMethodCall(interfaceName, "concatenate");
@ -1009,7 +1104,7 @@ On the convenience API level, the call statement starts with `callMethodAsync()`
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
/* ... */ /* ... */
auto callback = [](const sdbus::Error* error, const std::string& concatenatedString) auto callback = [](const sdbus::Error* error, const std::string& concatenatedString)
{ {
if (error == nullptr) // No error if (error == nullptr) // No error
@ -1036,11 +1131,11 @@ int main(int argc, char *argv[])
} }
``` ```
When the `Error` pointer is zero, it means that no D-Bus error occurred while making the call, and subsequent arguments are valid D-Bus method return values. Non-zero `Error` pointer, however, points to the valid `Error` instance, meaning that an error occurred during the call (and subsequent arguments are simply default-constructed). Error name and message can then be read out by the client from `Error` instance. When the `Error` pointer is zero, it means that no D-Bus error occurred while making the call, and subsequent arguments are valid D-Bus method return values. Non-zero `Error` pointer, however, points to the valid `Error` instance, meaning that an error occurred during the call (and subsequent arguments are simply default-constructed). Error name and message can then be read out by the client from `Error` instance.
### Marking client-side async methods in the IDL ### Marking client-side async methods in the IDL
sdbus-c++ stub generator can generate stub code for client-side async methods. We just need to annotate the method with the `annotate` element having the "org.freedesktop.DBus.Method.Async" name. The element value must be either "client" (async on the client-side only) or "clientserver" (async method on both client- and server-side): sdbus-c++ stub generator can generate stub code for client-side async methods. We just need to annotate the method with `org.freedesktop.DBus.Method.Async`. The annotation element value must be either `client` (async on the client-side only) or `clientserver` (async method on both client- and server-side):
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -1060,12 +1155,33 @@ sdbus-c++ stub generator can generate stub code for client-side async methods. W
</node> </node>
``` ```
For each client-side async method, a corresponding `on<MethodName>Reply` pure virtual function, where <MethodName> is capitalized D-Bus method name, is generated in the generated proxy class. This function is the callback invoked when the D-Bus method reply arrives, and must be provided a body by overriding it in the implementation class. For each client-side async method, a corresponding `on<MethodName>Reply` pure virtual function, where `<MethodName>` is the capitalized D-Bus method name, is generated in the generated proxy class. This function is the callback invoked when the D-Bus method reply arrives, and must be provided a body by overriding it in the implementation class.
So in the specific example above, the stub generator will generate a `Concatenator_proxy` class similar to one shown in a [dedicated section above](#concatenator-client-glueh), with the difference that it will also generate an additional `virtual void onConcatenateReply(const sdbus::Error* error, const std::string& concatenatedString);` method, which we shall override in derived `ConcatenatorProxy`. So in the specific example above, the stub generator will generate a `Concatenator_proxy` class similar to one shown in a [dedicated section above](#concatenator-client-glueh), with the difference that it will also generate an additional `virtual void onConcatenateReply(const sdbus::Error* error, const std::string& concatenatedString);` method, which we shall override in derived `ConcatenatorProxy`.
For a real example of a client-side asynchronous D-Bus method, please look at sdbus-c++ [stress tests](/tests/stresstests). For a real example of a client-side asynchronous D-Bus method, please look at sdbus-c++ [stress tests](/tests/stresstests).
## Method call timeout
Annotate the element with `org.freedesktop.DBus.Method.Timeout` in order to specify the timeout value for the method call. The value should be a number of microseconds or number with duration literal (`us`/`ms`/`s`/`min`). Optionally combine it with `org.freedesktop.DBus.Method.Async`.
```xml
<?xml version="1.0" encoding="UTF-8"?>
<node>
<interface name="org.bluez.Device1">
<method name="Connect">
<annotation name="org.freedesktop.DBus.Method.Async" value="client"/>
<annotation name="org.freedesktop.DBus.Method.Timeout" value="3000ms"/>
</method>
<method name="Disconnect">
<annotation name="org.freedesktop.DBus.Method.Async" value="client"/>
<annotation name="org.freedesktop.DBus.Method.Timeout" value="2000000"/> <!-- 2000000us -->
</method>
</interface>
</node>
```
Using D-Bus properties Using D-Bus properties
---------------------- ----------------------
@ -1156,12 +1272,19 @@ sdbus-c++ provides support for standard D-Bus interfaces. These are:
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. 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. Pre-generated `*_proxy` and `*_adaptor` convenience classes for these standard interfaces are located in `sdbus-c++/StandardInterfaces.h`. To use them, we simply have to add them 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. For example, for our `Concatenator` example above in this tutorial, we may want to conveniently emit a `PropertyChanged` signal under `org.freedesktop.DBus.Properties` interface. First, we must augment our `Concatenator` class to also inherit from `org.freedesktop.DBus.Properties` interface: `class Concatenator : public sdbus::AdaptorInterfaces<org::sdbuscpp::Concatenator_adaptor, sdbus::Properties_adaptor> {...};`, and then 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. 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.
Working examples of using standard D-Bus interfaces can be found in [sdbus-c++ integration tests](/tests/integrationtests/DBusStandardInterfacesTests.cpp) or the [examples](/examples) directory.
Support for match rules
-----------------------
`IConnection` class provides `addMatch` method that you can use to install match rules. An associated callback handler will be called upon an incoming message matching given match rule. There is support for both client-owned and floating (library-owned) match rules. Consult `IConnection` header or sdbus-c++ doxygen documentation for more information.
Conclusion Conclusion
---------- ----------

6
examples/CMakeLists.txt Normal file
View File

@ -0,0 +1,6 @@
add_executable(obj-manager-server org.freedesktop.DBus.ObjectManager/obj-manager-server.cpp)
target_link_libraries(obj-manager-server sdbus-c++)
add_executable(obj-manager-client org.freedesktop.DBus.ObjectManager/obj-manager-client.cpp)
target_link_libraries(obj-manager-client sdbus-c++)

View File

@ -0,0 +1,50 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__examplemanager_planet1_client_glue_h__proxy__H__
#define __sdbuscpp__examplemanager_planet1_client_glue_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace sdbuscpp {
namespace ExampleManager {
class Planet1_proxy
{
public:
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.ExampleManager.Planet1";
protected:
Planet1_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
}
~Planet1_proxy() = default;
public:
uint64_t GetPopulation()
{
uint64_t result;
proxy_.callMethod("GetPopulation").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
public:
std::string Name()
{
return proxy_.getProperty("Name").onInterface(INTERFACE_NAME);
}
private:
sdbus::IProxy& proxy_;
};
}}} // namespaces
#endif

View File

@ -0,0 +1,44 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__examplemanager_planet1_server_glue_h__adaptor__H__
#define __sdbuscpp__examplemanager_planet1_server_glue_h__adaptor__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace sdbuscpp {
namespace ExampleManager {
class Planet1_adaptor
{
public:
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.ExampleManager.Planet1";
protected:
Planet1_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("GetPopulation").onInterface(INTERFACE_NAME).withOutputParamNames("population").implementedAs([this](){ return this->GetPopulation(); });
object_.registerProperty("Name").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Name(); });
}
~Planet1_adaptor() = default;
private:
virtual uint64_t GetPopulation() = 0;
private:
virtual std::string Name() = 0;
private:
sdbus::IObject& object_;
};
}}} // namespaces
#endif

View File

@ -0,0 +1,110 @@
/**
* Example of a D-Bus client which implements org.freedesktop.DBus.ObjectManager
*
* The example uses the generated stub API layer to listen to interfaces added to new objects under
* "org.sdbuscpp.examplemanager". If added, we access "org.sdbuscpp.ExampleManager.Planet1" to print
* info like this:
* /org/sdbuscpp/examplemanager/Planet1/Earth added: org.sdbuscpp.ExampleManager.Planet1
* Earth has a population of 7874965825.
*
*/
#include "examplemanager-planet1-client-glue.h"
#include <sdbus-c++/sdbus-c++.h>
#include <iostream>
#include <thread>
class PlanetProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::ExampleManager::Planet1_proxy >
{
public:
PlanetProxy(sdbus::IConnection& connection, std::string destination, std::string path)
: ProxyInterfaces(connection, std::move(destination), std::move(path))
{
registerProxy();
}
~PlanetProxy()
{
unregisterProxy();
}
};
class ManagerProxy final : public sdbus::ProxyInterfaces< sdbus::ObjectManager_proxy >
{
public:
ManagerProxy(sdbus::IConnection& connection, const std::string& destination, std::string path)
: ProxyInterfaces(connection, destination, std::move(path))
, m_connection(connection)
, m_destination(destination)
{
registerProxy();
}
~ManagerProxy()
{
unregisterProxy();
}
void handleExistingObjects()
{
std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> objectsInterfacesAndProperties;
objectsInterfacesAndProperties = GetManagedObjects();
for (const auto& [object, interfacesAndProperties] : objectsInterfacesAndProperties) {
onInterfacesAdded(object, interfacesAndProperties);
}
}
private:
void onInterfacesAdded( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties) override
{
std::cout << objectPath << " added:\t";
for (const auto& [interface, _] : interfacesAndProperties) {
std::cout << interface << " ";
}
std::cout << std::endl;
// Parse and print some more info
auto planetInterface = interfacesAndProperties.find(org::sdbuscpp::ExampleManager::Planet1_proxy::INTERFACE_NAME);
if (planetInterface == interfacesAndProperties.end()) {
return;
}
const auto& properties = planetInterface->second;
// get a property which was passed as part of the signal.
const auto& name = properties.at("Name").get<std::string>();
// or create a proxy instance to the newly added object.
PlanetProxy planet(m_connection, m_destination, objectPath);
std::cout << name << " has a population of " << planet.GetPopulation() << ".\n" << std::endl;
}
void onInterfacesRemoved( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces) override
{
std::cout << objectPath << " removed:\t";
for (const auto& interface : interfaces) {
std::cout << interface << " ";
}
std::cout << std::endl;
}
sdbus::IConnection& m_connection;
std::string m_destination;
};
int main()
{
auto connection = sdbus::createSessionBusConnection();
auto managerProxy = std::make_unique<ManagerProxy>(*connection, "org.sdbuscpp.examplemanager", "/org/sdbuscpp/examplemanager");
try {
managerProxy->handleExistingObjects();
}
catch (const sdbus::Error& e) {
if (e.getName() == "org.freedesktop.DBus.Error.ServiceUnknown") {
std::cout << "Waiting for server to start ..." << std::endl;
}
}
connection->enterEventLoop();
return 0;
}

View File

@ -0,0 +1,108 @@
/**
* Example of a D-Bus server which implements org.freedesktop.DBus.ObjectManager
*
* The example uses the generated stub API layer to register an object manager under "org.sdbuscpp.examplemanager"
* and add objects underneath which implement "org.sdbuscpp.ExampleManager.Planet1".
*
* We add and remove objects after a few seconds and print info like this:
* Creating PlanetAdaptor in 5 4 3 2 1
* Creating PlanetAdaptor in 5 4 3 2 1
* Creating PlanetAdaptor in 5 4 3 2 1
* Removing PlanetAdaptor in 5 4 3 2 1
* Removing PlanetAdaptor in 5 4 3 2 1
*/
#include "examplemanager-planet1-server-glue.h"
#include <sdbus-c++/sdbus-c++.h>
#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
class ManagerAdaptor : public sdbus::AdaptorInterfaces< sdbus::ObjectManager_adaptor >
{
public:
ManagerAdaptor(sdbus::IConnection& connection, std::string path)
: AdaptorInterfaces(connection, std::move(path))
{
registerAdaptor();
}
~ManagerAdaptor()
{
unregisterAdaptor();
}
};
class PlanetAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::ExampleManager::Planet1_adaptor,
sdbus::ManagedObject_adaptor,
sdbus::Properties_adaptor >
{
public:
PlanetAdaptor(sdbus::IConnection& connection, std::string path, std::string name, uint64_t poulation)
: AdaptorInterfaces(connection, std::move(path))
, m_name(std::move(name))
, m_population(poulation)
{
registerAdaptor();
emitInterfacesAddedSignal({org::sdbuscpp::ExampleManager::Planet1_adaptor::INTERFACE_NAME});
}
~PlanetAdaptor()
{
emitInterfacesRemovedSignal({org::sdbuscpp::ExampleManager::Planet1_adaptor::INTERFACE_NAME});
unregisterAdaptor();
}
uint64_t GetPopulation() override
{
return m_population;
}
std::string Name() override
{
return m_name;
}
private:
std::string m_name;
uint64_t m_population;
};
void printCountDown(const std::string& message, int seconds)
{
std::cout << message << std::flush;
for (int i = seconds; i > 0; i--) {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << i << " " << std::flush;
}
std::cout << std::endl;
}
int main()
{
auto connection = sdbus::createSessionBusConnection();
connection->requestName("org.sdbuscpp.examplemanager");
connection->enterEventLoopAsync();
auto manager = std::make_unique<ManagerAdaptor>(*connection, "/org/sdbuscpp/examplemanager");
while (true)
{
printCountDown("Creating PlanetAdaptor in ", 5);
auto earth = std::make_unique<PlanetAdaptor>(*connection, "/org/sdbuscpp/examplemanager/Planet1/Earth", "Earth", 7'874'965'825);
printCountDown("Creating PlanetAdaptor in ", 5);
auto trantor = std::make_unique<PlanetAdaptor>(*connection, "/org/sdbuscpp/examplemanager/Planet1/Trantor", "Trantor", 40'000'000'000);
printCountDown("Creating PlanetAdaptor in ", 5);
auto laconia = std::make_unique<PlanetAdaptor>(*connection, "/org/sdbuscpp/examplemanager/Planet1/Laconia", "Laconia", 231'721);
printCountDown("Removing PlanetAdaptor in ", 5);
earth.reset();
printCountDown("Removing PlanetAdaptor in ", 5);
trantor.reset();
printCountDown("Removing PlanetAdaptor in ", 5);
laconia.reset();
}
connection->releaseName("org.sdbuscpp.examplemanager");
connection->leaveEventLoop();
return 0;
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.sdbuscpp.ExampleManager.Planet1">
<!--
@brief get the population of this planet
@param [out] population of the planet
-->
<method name="GetPopulation">
<arg name="population" type="t" direction="out" />
</method>
<!--
@brief This planet's name
-->
<property name="Name" type="s" access="read"/>
</interface>
</node>

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file AdaptorInterfaces.h * @file AdaptorInterfaces.h
* *
@ -141,6 +141,7 @@ namespace sdbus {
protected: protected:
using base_type = AdaptorInterfaces; using base_type = AdaptorInterfaces;
~AdaptorInterfaces() = default;
}; };
} }

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file ConvenienceApiClasses.h * @file ConvenienceApiClasses.h
* *
@ -216,6 +216,17 @@ namespace sdbus {
std::string interfaceName_; std::string interfaceName_;
}; };
class SignalUnsubscriber
{
public:
SignalUnsubscriber(IProxy& proxy, const std::string& signalName);
void onInterface(std::string interfaceName);
private:
IProxy& proxy_;
const std::string& signalName_;
};
class PropertyGetter class PropertyGetter
{ {
public: public:

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file ConvenienceApiClasses.inl * @file ConvenienceApiClasses.inl
* *
@ -588,7 +588,20 @@ namespace sdbus {
// Deserialize input arguments from the message into the tuple (if no error occurred). // Deserialize input arguments from the message into the tuple (if no error occurred).
if (error == nullptr) if (error == nullptr)
reply >> args; {
try
{
reply >> args;
}
catch (const Error& e)
{
// Catch message unpack exceptions and pass them to the callback
// in the expected manner to avoid propagating them up the call
// stack to the event loop.
sdbus::apply(callback, &e, args);
return;
}
}
// Invoke callback with input arguments from the tuple. // Invoke callback with input arguments from the tuple.
sdbus::apply(callback, error, args); sdbus::apply(callback, error, args);
@ -627,14 +640,52 @@ namespace sdbus {
// as a storage for the argument values deserialized from the signal message. // as a storage for the argument values deserialized from the signal message.
tuple_of_function_input_arg_types_t<_Function> signalArgs; tuple_of_function_input_arg_types_t<_Function> signalArgs;
// Deserialize input arguments from the signal message into the tuple // The signal handler can take pure signal parameters only, or an additional `const Error*` as its first
signal >> signalArgs; // parameter. In the former case, if the deserialization fails (e.g. due to signature mismatch),
// the failure is ignored (and signal simply dropped). In the latter case, the deserialization failure
// will be communicated as a non-zero Error pointer to the client's signal handler.
if constexpr (has_error_param_v<_Function>)
{
// Deserialize input arguments from the signal message into the tuple
try
{
signal >> signalArgs;
}
catch (const sdbus::Error& e)
{
// Invoke callback with error argument and input arguments from the tuple.
sdbus::apply(callback, &e, signalArgs);
}
// Invoke callback with input arguments from the tuple. // Invoke callback with no error and input arguments from the tuple.
sdbus::apply(callback, signalArgs); sdbus::apply(callback, nullptr, signalArgs);
}
else
{
// Deserialize input arguments from the signal message into the tuple
signal >> signalArgs;
// Invoke callback with input arguments from the tuple.
sdbus::apply(callback, signalArgs);
}
}); });
} }
/*** ------------------ ***/
/*** SignalUnsubscriber ***/
/*** ------------------ ***/
inline SignalUnsubscriber::SignalUnsubscriber(IProxy& proxy, const std::string& signalName)
: proxy_(proxy)
, signalName_(signalName)
{
}
inline void SignalUnsubscriber::onInterface(std::string interfaceName)
{
proxy_.unregisterSignalHandler(interfaceName, signalName_);
}
/*** -------------- ***/ /*** -------------- ***/
/*** PropertyGetter ***/ /*** PropertyGetter ***/
/*** -------------- ***/ /*** -------------- ***/

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file Error.h * @file Error.h
* *
@ -43,6 +43,11 @@ namespace sdbus {
: public std::runtime_error : public std::runtime_error
{ {
public: public:
explicit Error(const std::string& name, const char* message = nullptr)
: Error(name, std::string(message ? message : ""))
{
}
Error(const std::string& name, const std::string& message) Error(const std::string& name, const std::string& message)
: std::runtime_error("[" + name + "] " + message) : std::runtime_error("[" + name + "] " + message)
, name_(name) , name_(name)

View File

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

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file IConnection.h * @file IConnection.h
* *
@ -27,10 +27,12 @@
#ifndef SDBUS_CXX_ICONNECTION_H_ #ifndef SDBUS_CXX_ICONNECTION_H_
#define SDBUS_CXX_ICONNECTION_H_ #define SDBUS_CXX_ICONNECTION_H_
#include <sdbus-c++/TypeTraits.h>
#include <string> #include <string>
#include <memory> #include <memory>
#include <chrono> #include <chrono>
#include <cstdint> #include <cstdint>
#include <optional>
namespace sdbus { namespace sdbus {
@ -47,11 +49,66 @@ namespace sdbus {
class IConnection class IConnection
{ {
public: public:
/*!
* Poll Data for external event loop implementations.
*
* To integrate sdbus with your app's own custom event handling system
* you can use this method to query which file descriptors, poll events
* and timeouts you should add to your app's poll(2), or select(2)
* call in your main event loop.
*
* If you are unsure what this all means then use
* enterEventLoop() or enterEventLoopAsync() instead.
*
* See: getEventLoopPollData()
*/
struct PollData struct PollData
{ {
/*!
* The read fd to be monitored by the event loop.
*/
int fd; int fd;
/*!
* The events to use for poll(2) alongside fd.
*/
short int events; short int events;
/*!
* Absolute timeout value in micro seconds and based of CLOCK_MONOTONIC.
*/
uint64_t timeout_usec; uint64_t timeout_usec;
/*!
* The read fd to be monitored by the event loop.
*/
int event_fd;
/*!
* Get the event poll timeout.
*
* The timeout is an absolute value based of CLOCK_MONOTONIC.
*
* @return a duration since the CLOCK_MONOTONIC epoch started.
*/
[[nodiscard]] std::chrono::microseconds getAbsoluteTimeout() const
{
return std::chrono::microseconds(timeout_usec);
}
/*!
* Get the timeout as relative value from now
*
* @return std::nullopt if the timeout is indefinite. A duration otherwise.
*/
[[nodiscard]] std::optional<std::chrono::microseconds> getRelativeTimeout() const;
/*!
* Get a converted, relative timeout which can be passed as argument 'timeout' to poll(2)
*
* @return -1 if the timeout is indefinite. 0 if the poll(2) shouldn't block. An integer in milli
* seconds otherwise.
*/
[[nodiscard]] int getPollTimeout() const;
}; };
virtual ~IConnection() = default; virtual ~IConnection() = default;
@ -115,9 +172,15 @@ namespace sdbus {
* the connection. This is a convenient way to interrogate a connection * the connection. This is a convenient way to interrogate a connection
* to see what objects it has. * to see what objects it has.
* *
* This call creates a floating registration. The ObjectManager will
* be there for the object path until the connection is destroyed.
*
* Another, recommended way to add object managers is directly through
* IObject API.
*
* @throws sdbus::Error in case of failure * @throws sdbus::Error in case of failure
*/ */
virtual void addObjectManager(const std::string& objectPath) = 0; [[deprecated("Use one of other addObjectManager overloads")]] virtual void addObjectManager(const std::string& objectPath) = 0;
/*! /*!
* @brief Returns fd, I/O events and timeout data you can pass to poll * @brief Returns fd, I/O events and timeout data you can pass to poll
@ -190,6 +253,64 @@ namespace sdbus {
*/ */
virtual uint64_t getMethodCallTimeout() const = 0; virtual uint64_t getMethodCallTimeout() const = 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.
*
* This call creates a floating registration. The ObjectManager will
* be there for the object path until the connection is destroyed.
*
* Another, recommended way to add object managers is directly through
* IObject API.
*
* @throws sdbus::Error in case of failure
*/
virtual void addObjectManager(const std::string& objectPath, floating_slot_t) = 0;
/*!
* @brief Adds a match rule for incoming message dispatching
*
* @param[in] match Match expression to filter incoming D-Bus message
* @param[in] callback Callback handler to be called upon incoming D-Bus message matching the rule
* @return RAII-style slot handle representing the ownership of the subscription
*
* The method installs a match rule for messages received on the specified bus connection.
* The syntax of the match rule expression passed in match is described in the D-Bus specification.
* The specified handler function callback is called for each incoming message matching the specified
* expression. The match is installed synchronously when connected to a bus broker, i.e. the call
* sends a control message requested the match to be added to the broker and waits until the broker
* confirms the match has been installed successfully.
*
* Simply let go of the slot instance to uninstall the match rule from the bus connection. The slot
* must not outlive the connection for the slot is associated with it.
*
* For more information, consult `man sd_bus_add_match`.
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual Slot addMatch(const std::string& match, message_handler callback) = 0;
/*!
* @brief Adds a floating match rule for incoming message dispatching
*
* @param[in] match Match expression to filter incoming D-Bus message
* @param[in] callback Callback handler to be called upon incoming D-Bus message matching the rule
* @param[in] Floating slot tag
*
* The method installs a floating match rule for messages received on the specified bus connection.
* Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule
* is bound to the lifetime of the bus connection.
*
* Refer to the @c addMatch(const std::string& match, message_handler callback) documentation for more
* information.
*
* @throws sdbus::Error in case of failure
*/
virtual void addMatch(const std::string& match, message_handler callback, floating_slot_t) = 0;
/*! /*!
* @copydoc IConnection::enterEventLoop() * @copydoc IConnection::enterEventLoop()
* *
@ -247,7 +368,7 @@ namespace sdbus {
} }
/*! /*!
* @brief Creates/opens D-Bus system connection * @brief Creates/opens D-Bus system bus connection
* *
* @return Connection instance * @return Connection instance
* *
@ -256,7 +377,7 @@ namespace sdbus {
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createConnection(); [[nodiscard]] std::unique_ptr<sdbus::IConnection> createConnection();
/*! /*!
* @brief Creates/opens D-Bus system connection with a name * @brief Creates/opens D-Bus system bus connection with a name
* *
* @param[in] name Name to request on the connection after its opening * @param[in] name Name to request on the connection after its opening
* @return Connection instance * @return Connection instance
@ -266,7 +387,26 @@ namespace sdbus {
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createConnection(const std::string& name); [[nodiscard]] std::unique_ptr<sdbus::IConnection> createConnection(const std::string& name);
/*! /*!
* @brief Creates/opens D-Bus system connection * @brief Creates/opens D-Bus session bus connection when in a user context, and a system bus connection, otherwise.
*
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createDefaultBusConnection();
/*!
* @brief Creates/opens D-Bus session bus connection with a name when in a user context, and a system bus connection with a name, otherwise.
*
* @param[in] name Name to request on the connection after its opening
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createDefaultBusConnection(const std::string& name);
/*!
* @brief Creates/opens D-Bus system bus connection
* *
* @return Connection instance * @return Connection instance
* *
@ -275,7 +415,7 @@ namespace sdbus {
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSystemBusConnection(); [[nodiscard]] std::unique_ptr<sdbus::IConnection> createSystemBusConnection();
/*! /*!
* @brief Creates/opens D-Bus system connection with a name * @brief Creates/opens D-Bus system bus connection with a name
* *
* @param[in] name Name to request on the connection after its opening * @param[in] name Name to request on the connection after its opening
* @return Connection instance * @return Connection instance
@ -285,7 +425,7 @@ namespace sdbus {
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name); [[nodiscard]] std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name);
/*! /*!
* @brief Creates/opens D-Bus session connection * @brief Creates/opens D-Bus session bus connection
* *
* @return Connection instance * @return Connection instance
* *
@ -294,7 +434,7 @@ namespace sdbus {
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnection(); [[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnection();
/*! /*!
* @brief Creates/opens D-Bus session connection with a name * @brief Creates/opens D-Bus session bus connection with a name
* *
* @param[in] name Name to request on the connection after its opening * @param[in] name Name to request on the connection after its opening
* @return Connection instance * @return Connection instance
@ -303,6 +443,18 @@ namespace sdbus {
*/ */
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name); [[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name);
/*!
* @brief Creates/opens D-Bus session bus connection at a custom address
*
* @param[in] address ";"-separated list of addresses of bus brokers to try to connect
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*
* Consult manual pages for `sd_bus_set_address` of the underlying sd-bus library for more information.
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnectionWithAddress(const std::string& address);
/*! /*!
* @brief Creates/opens D-Bus system connection on a remote host using ssh * @brief Creates/opens D-Bus system connection on a remote host using ssh
* *

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file IObject.h * @file IObject.h
* *
@ -440,6 +440,22 @@ namespace sdbus {
* @brief Returns object path of the underlying DBus object * @brief Returns object path of the underlying DBus object
*/ */
virtual const std::string& getObjectPath() const = 0; virtual const std::string& getObjectPath() const = 0;
/*!
* @brief Provides currently processed D-Bus message
*
* This method provides immutable access to the currently processed incoming D-Bus message.
* "Currently processed" means that the registered callback handler(s) for that message
* are being invoked. This method is meant to be called from within a callback handler
* (e.g. D-Bus method implementation handler). In such a case it is guaranteed to return
* a valid pointer to the D-Bus message for which the handler is called. If called from other
* contexts/threads, it may return a nonzero pointer or a nullptr, depending on whether a message
* was processed at the time of call or not, but the value is nondereferencable, since the pointed-to
* message may have gone in the meantime.
*
* @return A pointer to the currently processed D-Bus message
*/
virtual const Message* getCurrentlyProcessedMessage() const = 0;
}; };
// Out-of-line member definitions // Out-of-line member definitions

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file IProxy.h * @file IProxy.h
* *
@ -143,6 +143,17 @@ namespace sdbus {
, const std::string& signalName , const std::string& signalName
, signal_handler signalHandler ) = 0; , signal_handler signalHandler ) = 0;
/*!
* @brief Unregisters the handler of the desired signal
*
* @param[in] interfaceName Name of an interface that the signal belongs to
* @param[in] signalName Name of the signal
*
* @throws sdbus::Error in case of failure
*/
virtual void unregisterSignalHandler( const std::string& interfaceName
, const std::string& signalName ) = 0;
/*! /*!
* @brief Finishes the registration of signal handlers * @brief Finishes the registration of signal handlers
* *
@ -229,6 +240,23 @@ namespace sdbus {
*/ */
[[nodiscard]] SignalSubscriber uponSignal(const std::string& signalName); [[nodiscard]] SignalSubscriber uponSignal(const std::string& signalName);
/*!
* @brief Unregisters signal handler of a given signal of the proxied D-Bus object
*
* @param[in] signalName Name of the signal
* @return A helper object for convenient unregistration of the signal handler
*
* This is a high-level, convenience way of unregistering a D-Bus signal's handler.
*
* Example of use:
* @code
* object_.muteSignal("fooSignal").onInterface("com.kistler.foo");
* @endcode
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] SignalUnsubscriber muteSignal(const std::string& signalName);
/*! /*!
* @brief Gets value of a property of the proxied D-Bus object * @brief Gets value of a property of the proxied D-Bus object
* *
@ -267,10 +295,33 @@ namespace sdbus {
*/ */
[[nodiscard]] PropertySetter setProperty(const std::string& propertyName); [[nodiscard]] PropertySetter setProperty(const std::string& propertyName);
/*!
* @brief Provides D-Bus connection used by the proxy
*
* @return Reference to the D-Bus connection
*/
virtual sdbus::IConnection& getConnection() const = 0;
/*! /*!
* @brief Returns object path of the underlying DBus object * @brief Returns object path of the underlying DBus object
*/ */
virtual const std::string& getObjectPath() const = 0; virtual const std::string& getObjectPath() const = 0;
/*!
* @brief Provides currently processed D-Bus message
*
* This method provides immutable access to the currently processed incoming D-Bus message.
* "Currently processed" means that the registered callback handler(s) for that message
* are being invoked. This method is meant to be called from within a callback handler
* (e.g. from a D-Bus signal handler, or async method reply handler, etc.). In such a case it is
* guaranteed to return a valid pointer to the D-Bus message for which the handler is called.
* If called from other contexts/threads, it may return a nonzero pointer or a nullptr, depending
* on whether a message was processed at the time of call or not, but the value is nondereferencable,
* since the pointed-to message may have gone in the meantime.
*
* @return A pointer to the currently processed D-Bus message
*/
virtual const Message* getCurrentlyProcessedMessage() const = 0;
}; };
/********************************************//** /********************************************//**
@ -286,6 +337,8 @@ namespace sdbus {
class PendingAsyncCall class PendingAsyncCall
{ {
public: public:
PendingAsyncCall() = default;
/*! /*!
* @brief Cancels the delivery of the pending asynchronous call result * @brief Cancels the delivery of the pending asynchronous call result
* *
@ -344,6 +397,11 @@ namespace sdbus {
return SignalSubscriber(*this, signalName); return SignalSubscriber(*this, signalName);
} }
inline SignalUnsubscriber IProxy::muteSignal(const std::string& signalName)
{
return SignalUnsubscriber(*this, signalName);
}
inline PropertyGetter IProxy::getProperty(const std::string& propertyName) inline PropertyGetter IProxy::getProperty(const std::string& propertyName)
{ {
return PropertyGetter(*this, propertyName); return PropertyGetter(*this, propertyName);

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file Message.h * @file Message.h
* *
@ -32,11 +32,11 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <map> #include <map>
#include <memory>
#include <utility> #include <utility>
#include <cstdint> #include <cstdint>
#include <cassert> #include <cassert>
#include <functional> #include <functional>
#include <sys/types.h>
// Forward declarations // Forward declarations
namespace sdbus { namespace sdbus {
@ -44,25 +44,21 @@ namespace sdbus {
class ObjectPath; class ObjectPath;
class Signature; class Signature;
template <typename... _ValueTypes> class Struct; template <typename... _ValueTypes> class Struct;
struct UnixFd; class UnixFd;
class MethodReply; class MethodReply;
namespace internal { namespace internal {
class ISdBus; class ISdBus;
class IConnection;
} }
} }
namespace sdbus { namespace sdbus {
// Assume the caller has already obtained message ownership
struct adopt_message_t { explicit adopt_message_t() = default; };
inline constexpr adopt_message_t adopt_message{};
/********************************************//** /********************************************//**
* @class Message * @class Message
* *
* Message represents a D-Bus message, which can be either method call message, * Message represents a D-Bus message, which can be either method call message,
* method reply message, signal message, or a plain message serving as a storage * method reply message, signal message, or a plain message.
* for serialized data.
* *
* Serialization and deserialization functions are provided for types supported * Serialization and deserialization functions are provided for types supported
* by D-Bus. * by D-Bus.
@ -130,6 +126,8 @@ namespace sdbus {
std::string getInterfaceName() const; std::string getInterfaceName() const;
std::string getMemberName() const; std::string getMemberName() const;
std::string getSender() const; std::string getSender() const;
std::string getPath() const;
std::string getDestination() const;
void peekType(std::string& type, std::string& contents) const; void peekType(std::string& type, std::string& contents) const;
bool isValid() const; bool isValid() const;
bool isEmpty() const; bool isEmpty() const;
@ -138,6 +136,14 @@ namespace sdbus {
void seal(); void seal();
void rewind(bool complete); void rewind(bool complete);
pid_t getCredsPid() const;
uid_t getCredsUid() const;
uid_t getCredsEuid() const;
gid_t getCredsGid() const;
gid_t getCredsEgid() const;
std::vector<gid_t> getCredsSupplementaryGids() const;
std::string getSELinuxContext() const;
class Factory; class Factory;
protected: protected:
@ -161,21 +167,17 @@ namespace sdbus {
mutable bool ok_{true}; mutable bool ok_{true};
}; };
struct dont_request_slot_t { explicit dont_request_slot_t() = default; };
inline constexpr dont_request_slot_t dont_request_slot{};
class MethodCall : public Message class MethodCall : public Message
{ {
using Message::Message; using Message::Message;
friend Factory; friend Factory;
public: public:
using Slot = std::unique_ptr<void, std::function<void(void*)>>;
MethodCall() = default; MethodCall() = default;
MethodReply send(uint64_t timeout) const; MethodReply send(uint64_t timeout) const;
void send(void* callback, void* userData, uint64_t timeout, dont_request_slot_t) const; [[deprecated("Use send overload with floating_slot instead")]] void send(void* callback, void* userData, uint64_t timeout, dont_request_slot_t) const;
void send(void* callback, void* userData, uint64_t timeout, floating_slot_t) const;
[[nodiscard]] Slot send(void* callback, void* userData, uint64_t timeout) const; [[nodiscard]] Slot send(void* callback, void* userData, uint64_t timeout) const;
MethodReply createReply() const; MethodReply createReply() const;
@ -184,9 +186,13 @@ namespace sdbus {
void dontExpectReply(); void dontExpectReply();
bool doesntExpectReply() const; bool doesntExpectReply() const;
protected:
MethodCall(void *msg, internal::ISdBus* sdbus, const internal::IConnection* connection, adopt_message_t) noexcept;
private: private:
MethodReply sendWithReply(uint64_t timeout = 0) const; MethodReply sendWithReply(uint64_t timeout = 0) const;
MethodReply sendWithNoReply() const; MethodReply sendWithNoReply() const;
const internal::IConnection* connection_{};
}; };
class MethodReply : public Message class MethodReply : public Message
@ -206,6 +212,7 @@ namespace sdbus {
public: public:
Signal() = default; Signal() = default;
void setDestination(const std::string& destination);
void send() const; void send() const;
}; };
@ -227,6 +234,7 @@ namespace sdbus {
PropertyGetReply() = default; PropertyGetReply() = default;
}; };
// Represents any of the above message types, or just a message that serves as a container for data
class PlainMessage : public Message class PlainMessage : public Message
{ {
using Message::Message; using Message::Message;

View File

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

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file ProxyInterfaces.h * @file ProxyInterfaces.h
* *
@ -175,6 +175,7 @@ namespace sdbus {
protected: protected:
using base_type = ProxyInterfaces; using base_type = ProxyInterfaces;
~ProxyInterfaces() = default;
}; };
} }

View File

@ -0,0 +1,67 @@
#pragma once
#include <boost/asio.hpp>
#include <boost/noncopyable.hpp>
#include <memory>
#include <sdbus-c++/sdbus-c++.h>
class SdbusAsio final : boost::noncopyable {
public:
explicit SdbusAsio(boost::asio::io_context& io_context,
std::unique_ptr<sdbus::IConnection> conn = sdbus::createDefaultBusConnection())
: conn_ { std::move(conn) }
, timer_ { io_context }
, dbus_desc_ { io_context }
, event_desc_ { io_context }
{
auto poll_data = conn_->getEventLoopPollData();
dbus_desc_.async_wait(boost::asio::posix::stream_descriptor::wait_read,
[this](const boost::system::error_code&) { processRead(); });
dbus_desc_.assign(poll_data.fd);
event_desc_.async_read_some(boost::asio::null_buffers(), [this](auto&, auto) { processEvent(); });
event_desc_.assign(poll_data.event_fd);
if (poll_data.timeout_usec != UINT64_MAX) {
timer_.async_wait([this](boost::system::error_code const&) { processTimeout(); });
timer_.expires_after(boost::posix_time::microsec(poll_data.timeout_usec));
}
}
std::shared_ptr<sdbus::IConnection> getConnection() { return conn_; }
private:
void process()
{
for (auto i = 0; i < DBUS_PROCESS_MAX; i++) {
if (!conn_->processPendingRequest()) {
break;
}
}
}
void processRead()
{
process();
dbus_desc_.async_wait(boost::asio::posix::stream_descriptor::wait_read,
[this](const boost::system::error_code&) { processRead(); });
}
void processEvent()
{
process();
event_desc_.async_read_some(boost::asio::null_buffers(), [this](auto, auto) { processEvent(); });
}
void processTimeout()
{
process();
timer_.async_wait([this](boost::system::error_code const&) { processTimeout(); });
}
static constexpr auto DBUS_PROCESS_MAX = 32;
std::shared_ptr<sdbus::IConnection> conn_;
boost::asio::deadline_timer timer_;
boost::asio::posix::stream_descriptor dbus_desc_;
boost::asio::posix::stream_descriptor event_desc_;
};

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file StandardInterfaces.h * @file StandardInterfaces.h
* *
@ -187,8 +187,8 @@ namespace sdbus {
}; };
// Adaptors for the above-listed standard D-Bus interfaces are not necessary because the functionality // Adaptors for the above-listed standard D-Bus interfaces are not necessary because the functionality
// is provided by underlying libsystemd implementation. The exception is Properties_adaptor and // is provided by underlying libsystemd implementation. The exception is Properties_adaptor,
// ObjectManager_adaptor, which provide convenience functionality to emit signals. // ObjectManager_adaptor and ManagedObject_adaptor, which provide convenience functionality to emit signals.
// Adaptor for properties // Adaptor for properties
class Properties_adaptor class Properties_adaptor
@ -218,13 +218,22 @@ namespace sdbus {
sdbus::IObject& object_; sdbus::IObject& object_;
}; };
// Adaptor for object manager /*!
* @brief Object Manager Convenience Adaptor
*
* Adding this class as _Interfaces.. template parameter of class AdaptorInterfaces
* implements the *GetManagedObjects()* method of the [org.freedesktop.DBus.ObjectManager.GetManagedObjects](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager)
* interface.
*
* Note that there can be multiple object managers in a path hierarchy. InterfacesAdded/InterfacesRemoved
* signals are sent from the closest object manager at either the same path or the closest parent path of an object.
*/
class ObjectManager_adaptor class ObjectManager_adaptor
{ {
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager"; static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
protected: protected:
ObjectManager_adaptor(sdbus::IObject& object) explicit ObjectManager_adaptor(sdbus::IObject& object)
: object_(object) : object_(object)
{ {
object_.addObjectManager(); object_.addObjectManager();
@ -232,22 +241,66 @@ namespace sdbus {
~ObjectManager_adaptor() = default; ~ObjectManager_adaptor() = default;
private:
sdbus::IObject& object_;
};
/*!
* @brief Managed Object Convenience Adaptor
*
* Adding this class as _Interfaces.. template parameter of class AdaptorInterfaces
* will extend the resulting object adaptor with emitInterfacesAddedSignal()/emitInterfacesRemovedSignal()
* according to org.freedesktop.DBus.ObjectManager.InterfacesAdded/.InterfacesRemoved.
*
* Note that objects which implement this adaptor require an object manager (e.g via ObjectManager_adaptor) to be
* instantiated on one of it's parent object paths or the same path. InterfacesAdded/InterfacesRemoved
* signals are sent from the closest object manager at either the same path or the closest parent path of an object.
*/
class ManagedObject_adaptor
{
protected:
explicit ManagedObject_adaptor(sdbus::IObject& object) : object_(object)
{
}
~ManagedObject_adaptor() = default;
public: public:
/*!
* @brief Emits InterfacesAdded signal for this object path
*
* See IObject::emitInterfacesAddedSignal().
*/
void emitInterfacesAddedSignal() void emitInterfacesAddedSignal()
{ {
object_.emitInterfacesAddedSignal(); object_.emitInterfacesAddedSignal();
} }
/*!
* @brief Emits InterfacesAdded signal for this object path
*
* See IObject::emitInterfacesAddedSignal().
*/
void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces) void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces)
{ {
object_.emitInterfacesAddedSignal(interfaces); object_.emitInterfacesAddedSignal(interfaces);
} }
/*!
* @brief Emits InterfacesRemoved signal for this object path
*
* See IObject::emitInterfacesRemovedSignal().
*/
void emitInterfacesRemovedSignal() void emitInterfacesRemovedSignal()
{ {
object_.emitInterfacesRemovedSignal(); object_.emitInterfacesRemovedSignal();
} }
/*!
* @brief Emits InterfacesRemoved signal for this object path
*
* See IObject::emitInterfacesRemovedSignal().
*/
void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces) void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces)
{ {
object_.emitInterfacesRemovedSignal(interfaces); object_.emitInterfacesRemovedSignal(interfaces);

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file TypeTraits.h * @file TypeTraits.h
* *
@ -33,6 +33,7 @@
#include <map> #include <map>
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include <memory>
#include <tuple> #include <tuple>
// Forward declarations // Forward declarations
@ -41,10 +42,11 @@ namespace sdbus {
template <typename... _ValueTypes> class Struct; template <typename... _ValueTypes> class Struct;
class ObjectPath; class ObjectPath;
class Signature; class Signature;
struct UnixFd; class UnixFd;
class MethodCall; class MethodCall;
class MethodReply; class MethodReply;
class Signal; class Signal;
class Message;
class PropertySetCall; class PropertySetCall;
class PropertyGetReply; class PropertyGetReply;
template <typename... _Results> class Result; template <typename... _Results> class Result;
@ -53,12 +55,34 @@ namespace sdbus {
namespace sdbus { namespace sdbus {
// Callbacks from sdbus-c++
using method_callback = std::function<void(MethodCall msg)>; using method_callback = std::function<void(MethodCall msg)>;
using async_reply_handler = std::function<void(MethodReply& reply, const Error* error)>; using async_reply_handler = std::function<void(MethodReply& reply, const Error* error)>;
using signal_handler = std::function<void(Signal& signal)>; using signal_handler = std::function<void(Signal& signal)>;
using message_handler = std::function<void(Message& msg)>;
using property_set_callback = std::function<void(PropertySetCall& msg)>; using property_set_callback = std::function<void(PropertySetCall& msg)>;
using property_get_callback = std::function<void(PropertyGetReply& reply)>; using property_get_callback = std::function<void(PropertyGetReply& reply)>;
// Type-erased RAII-style handle to callbacks/subscriptions registered to sdbus-c++
using Slot = std::unique_ptr<void, std::function<void(void*)>>;
// Tag specifying that an owning slot handle shall be returned from the function
struct request_slot_t { explicit request_slot_t() = default; };
inline constexpr request_slot_t request_slot{};
// Tag specifying that the library shall own the slot resulting from the call of the function (so-called floating slot)
struct floating_slot_t { explicit floating_slot_t() = default; };
inline constexpr floating_slot_t floating_slot{};
// Deprecated name for the above -- a floating slot
struct dont_request_slot_t { explicit dont_request_slot_t() = default; };
[[deprecated("Replaced by floating_slot")]] inline constexpr dont_request_slot_t dont_request_slot{};
// Tag denoting the assumption that the caller has already obtained message ownership
struct adopt_message_t { explicit adopt_message_t() = default; };
inline constexpr adopt_message_t adopt_message{};
// Tag denoting the assumption that the caller has already obtained fd ownership
struct adopt_fd_t { explicit adopt_fd_t() = default; };
inline constexpr adopt_fd_t adopt_fd{};
// Template specializations for getting D-Bus signatures from C++ types
template <typename _T> template <typename _T>
struct signature_of struct signature_of
{ {
@ -378,12 +402,14 @@ namespace sdbus {
: public function_traits_base<_ReturnType, _Args...> : public function_traits_base<_ReturnType, _Args...>
{ {
static constexpr bool is_async = false; static constexpr bool is_async = false;
static constexpr bool has_error_param = false;
}; };
template <typename... _Args> template <typename... _Args>
struct function_traits<void(const Error*, _Args...)> struct function_traits<void(const Error*, _Args...)>
: public function_traits_base<void, _Args...> : public function_traits_base<void, _Args...>
{ {
static constexpr bool has_error_param = true;
}; };
template <typename... _Args, typename... _Results> template <typename... _Args, typename... _Results>
@ -443,6 +469,9 @@ namespace sdbus {
template <class _Function> template <class _Function>
constexpr auto is_async_method_v = function_traits<_Function>::is_async; constexpr auto is_async_method_v = function_traits<_Function>::is_async;
template <class _Function>
constexpr auto has_error_param_v = function_traits<_Function>::has_error_param;
template <typename _FunctionType> template <typename _FunctionType>
using function_arguments_t = typename function_traits<_FunctionType>::arguments_type; using function_arguments_t = typename function_traits<_FunctionType>::arguments_type;

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file Types.h * @file Types.h
* *
@ -154,6 +154,10 @@ namespace sdbus {
public: public:
using std::string::string; using std::string::string;
ObjectPath() = default; // Fixes gcc 6.3 error (default c-tor is not imported in above using declaration) ObjectPath() = default; // Fixes gcc 6.3 error (default c-tor is not imported in above using declaration)
ObjectPath(const ObjectPath&) = default; // Fixes gcc 8.3 error (deleted copy constructor)
ObjectPath(ObjectPath&&) = default; // Enable move - user-declared copy ctor prevents implicit creation
ObjectPath& operator = (const ObjectPath&) = default; // Fixes gcc 8.3 error (deleted copy assignment)
ObjectPath& operator = (ObjectPath&&) = default; // Enable move - user-declared copy assign prevents implicit creation
ObjectPath(std::string path) ObjectPath(std::string path)
: std::string(std::move(path)) : std::string(std::move(path))
{} {}
@ -171,15 +175,16 @@ namespace sdbus {
public: public:
using std::string::string; using std::string::string;
Signature() = default; // Fixes gcc 6.3 error (default c-tor is not imported in above using declaration) Signature() = default; // Fixes gcc 6.3 error (default c-tor is not imported in above using declaration)
Signature(const Signature&) = default; // Fixes gcc 8.3 error (deleted copy constructor)
Signature(Signature&&) = default; // Enable move - user-declared copy ctor prevents implicit creation
Signature& operator = (const Signature&) = default; // Fixes gcc 8.3 error (deleted copy assignment)
Signature& operator = (Signature&&) = default; // Enable move - user-declared copy assign prevents implicit creation
Signature(std::string path) Signature(std::string path)
: std::string(std::move(path)) : std::string(std::move(path))
{} {}
using std::string::operator=; using std::string::operator=;
}; };
struct adopt_fd_t { explicit adopt_fd_t() = default; };
inline constexpr adopt_fd_t adopt_fd{};
/********************************************//** /********************************************//**
* @struct UnixFd * @struct UnixFd
* *

View File

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

View File

@ -5,7 +5,7 @@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
Name: @PROJECT_NAME@ Name: @PROJECT_NAME@
Description: C++ library on top of sd-bus, a systemd D-Bus library Description: C++ library on top of sd-bus, a systemd D-Bus library
Requires: libsystemd Requires@PKGCONFIG_REQS@: libsystemd
Version: @SDBUSCPP_VERSION@ Version: @SDBUSCPP_VERSION@
Libs: -L${libdir} -l@PROJECT_NAME@ Libs: -L${libdir} -l@PROJECT_NAME@
Cflags: -I${includedir} Cflags: -I${includedir}

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file Connection.cpp * @file Connection.cpp
* *
@ -27,6 +27,7 @@
#include "Connection.h" #include "Connection.h"
#include "SdBus.h" #include "SdBus.h"
#include "MessageUtils.h" #include "MessageUtils.h"
#include "Utils.h"
#include <sdbus-c++/Message.h> #include <sdbus-c++/Message.h>
#include <sdbus-c++/Error.h> #include <sdbus-c++/Error.h>
#include "ScopeGuard.h" #include "ScopeGuard.h"
@ -44,6 +45,11 @@ Connection::Connection(std::unique_ptr<ISdBus>&& interface, const BusFactory& bu
assert(iface_ != nullptr); assert(iface_ != nullptr);
} }
Connection::Connection(std::unique_ptr<ISdBus>&& interface, default_bus_t)
: Connection(std::move(interface), [this](sd_bus** bus){ return iface_->sd_bus_open(bus); })
{
}
Connection::Connection(std::unique_ptr<ISdBus>&& interface, system_bus_t) 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); }) : Connection(std::move(interface), [this](sd_bus** bus){ return iface_->sd_bus_open_system(bus); })
{ {
@ -54,18 +60,37 @@ Connection::Connection(std::unique_ptr<ISdBus>&& interface, session_bus_t)
{ {
} }
Connection::Connection(std::unique_ptr<ISdBus>&& interface, custom_session_bus_t, const std::string& address)
: Connection(std::move(interface), [&](sd_bus** bus) { return iface_->sd_bus_open_user_with_address(bus, address.c_str()); })
{
}
Connection::Connection(std::unique_ptr<ISdBus>&& interface, remote_system_bus_t, const std::string& host) 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(std::move(interface), [this, &host](sd_bus** bus){ return iface_->sd_bus_open_system_remote(bus, host.c_str()); })
{ {
} }
Connection::Connection(std::unique_ptr<ISdBus>&& interface, pseudo_bus_t)
: iface_(std::move(interface))
, bus_(openPseudoBus())
{
assert(iface_ != nullptr);
eventFd_.fd = eventfd(0, 0);
assert(eventFd_.fd >= 0);
}
Connection::~Connection() Connection::~Connection()
{ {
Connection::leaveEventLoop(); Connection::leaveEventLoop();
if (0 <= eventFd_.fd) {
close(eventFd_.fd);
}
} }
void Connection::requestName(const std::string& name) void Connection::requestName(const std::string& name)
{ {
SDBUS_CHECK_SERVICE_NAME(name);
auto r = iface_->sd_bus_request_name(bus_.get(), name.c_str(), 0); auto r = iface_->sd_bus_request_name(bus_.get(), name.c_str(), 0);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to request bus name", -r); SDBUS_THROW_ERROR_IF(r < 0, "Failed to request bus name", -r);
} }
@ -87,6 +112,7 @@ std::string Connection::getUniqueName() const
void Connection::enterEventLoop() void Connection::enterEventLoop()
{ {
loopThreadId_ = std::this_thread::get_id(); loopThreadId_ = std::this_thread::get_id();
SCOPE_EXIT{ loopThreadId_ = std::thread::id{}; };
std::lock_guard guard(loopMutex_); std::lock_guard guard(loopMutex_);
@ -100,8 +126,6 @@ void Connection::enterEventLoop()
if (!success) if (!success)
break; // Exit I/O event loop break; // Exit I/O event loop
} }
loopThreadId_ = std::thread::id{};
} }
void Connection::enterEventLoopAsync() void Connection::enterEventLoopAsync()
@ -118,11 +142,11 @@ void Connection::leaveEventLoop()
Connection::PollData Connection::getEventLoopPollData() const Connection::PollData Connection::getEventLoopPollData() const
{ {
ISdBus::PollData pollData; ISdBus::PollData pollData{};
auto r = iface_->sd_bus_get_poll_data(bus_.get(), &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); SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus poll data", -r);
return {pollData.fd, pollData.events, pollData.timeout_usec}; return {pollData.fd, pollData.events, pollData.timeout_usec, eventFd_.fd};
} }
const ISdBus& Connection::getSdBusInterface() const const ISdBus& Connection::getSdBusInterface() const
@ -137,16 +161,21 @@ ISdBus& Connection::getSdBusInterface()
void Connection::addObjectManager(const std::string& objectPath) void Connection::addObjectManager(const std::string& objectPath)
{ {
Connection::addObjectManager(objectPath, nullptr); Connection::addObjectManager(objectPath, floating_slot);
} }
SlotPtr Connection::addObjectManager(const std::string& objectPath, void* /*dummy*/) void Connection::addObjectManager(const std::string& objectPath, floating_slot_t)
{
auto r = iface_->sd_bus_add_object_manager(bus_.get(), nullptr, objectPath.c_str());
SDBUS_THROW_ERROR_IF(r < 0, "Failed to add object manager", -r);
}
Slot Connection::addObjectManager(const std::string& objectPath, request_slot_t)
{ {
sd_bus_slot *slot{}; sd_bus_slot *slot{};
auto r = iface_->sd_bus_add_object_manager( bus_.get() auto r = iface_->sd_bus_add_object_manager(bus_.get(), &slot, objectPath.c_str());
, &slot
, objectPath.c_str() );
SDBUS_THROW_ERROR_IF(r < 0, "Failed to add object manager", -r); SDBUS_THROW_ERROR_IF(r < 0, "Failed to add object manager", -r);
@ -171,10 +200,38 @@ uint64_t Connection::getMethodCallTimeout() const
return timeout; return timeout;
} }
SlotPtr Connection::addObjectVTable( const std::string& objectPath Slot Connection::addMatch(const std::string& match, message_handler callback)
, const std::string& interfaceName {
, const sd_bus_vtable* vtable auto matchInfo = std::make_unique<MatchInfo>(MatchInfo{std::move(callback), *this, {}});
, void* userData )
auto messageHandler = [](sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/) -> int
{
auto* matchInfo = static_cast<MatchInfo*>(userData);
auto message = Message::Factory::create<PlainMessage>(sdbusMessage, &matchInfo->connection.getSdBusInterface());
matchInfo->callback(message);
return 0;
};
auto r = iface_->sd_bus_add_match(bus_.get(), &matchInfo->slot, match.c_str(), std::move(messageHandler), matchInfo.get());
SDBUS_THROW_ERROR_IF(r < 0, "Failed to add match", -r);
return {matchInfo.release(), [this](void *ptr)
{
auto* matchInfo = static_cast<MatchInfo*>(ptr);
iface_->sd_bus_slot_unref(matchInfo->slot);
std::default_delete<MatchInfo>{}(matchInfo);
}};
}
void Connection::addMatch(const std::string& match, message_handler callback, floating_slot_t)
{
floatingMatchRules_.push_back(addMatch(match, std::move(callback)));
}
Slot Connection::addObjectVTable( const std::string& objectPath
, const std::string& interfaceName
, const sd_bus_vtable* vtable
, void* userData )
{ {
sd_bus_slot *slot{}; sd_bus_slot *slot{};
@ -217,7 +274,7 @@ MethodCall Connection::createMethodCall( const std::string& destination
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method call", -r); SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method call", -r);
return Message::Factory::create<MethodCall>(sdbusMsg, iface_.get(), adopt_message); return Message::Factory::create<MethodCall>(sdbusMsg, iface_.get(), this, adopt_message);
} }
Signal Connection::createSignal( const std::string& objectPath Signal Connection::createSignal( const std::string& objectPath
@ -289,15 +346,19 @@ void Connection::emitInterfacesRemovedSignal( const std::string& objectPath
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit InterfacesRemoved signal", -r); SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit InterfacesRemoved signal", -r);
} }
SlotPtr Connection::registerSignalHandler( const std::string& objectPath Slot Connection::registerSignalHandler( const std::string& sender
, const std::string& interfaceName , const std::string& objectPath
, const std::string& signalName , const std::string& interfaceName
, sd_bus_message_handler_t callback , const std::string& signalName
, void* userData ) , sd_bus_message_handler_t callback
, void* userData )
{ {
sd_bus_slot *slot{}; sd_bus_slot *slot{};
auto filter = composeSignalMatchFilter(objectPath, interfaceName, signalName); // Alternatively to our own composeSignalMatchFilter() implementation, we could use sd_bus_match_signal() from
// https://www.freedesktop.org/software/systemd/man/sd_bus_add_match.html .
// But this would require libsystemd v237 or higher.
auto filter = composeSignalMatchFilter(sender, objectPath, interfaceName, signalName);
auto r = iface_->sd_bus_add_match(bus_.get(), &slot, filter.c_str(), callback, userData); auto r = iface_->sd_bus_add_match(bus_.get(), &slot, filter.c_str(), callback, userData);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to register signal handler", -r); SDBUS_THROW_ERROR_IF(r < 0, "Failed to register signal handler", -r);
@ -345,6 +406,23 @@ Connection::BusPtr Connection::openBus(const BusFactory& busFactory)
return busPtr; return busPtr;
} }
Connection::BusPtr Connection::openPseudoBus()
{
sd_bus* bus{};
int r = iface_->sd_bus_new(&bus);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open pseudo bus", -r);
(void)iface_->sd_bus_start(bus);
// It is expected that sd_bus_start has failed here, returning -EINVAL, due to having
// not set a bus address, but it will leave the bus in an OPENING state, which enables
// us to create plain D-Bus messages as a local data storage (for Variant, for example),
// without dependency on real IPC communication with the D-Bus broker daemon.
SDBUS_THROW_ERROR_IF(r < 0 && r != -EINVAL, "Failed to start pseudo bus", -r);
return {bus, [this](sd_bus* bus){ return iface_->sd_bus_close_unref(bus); }};
}
void Connection::finishHandshake(sd_bus* bus) void Connection::finishHandshake(sd_bus* bus)
{ {
// Process all requests that are part of the initial handshake, // Process all requests that are part of the initial handshake,
@ -358,19 +436,42 @@ void Connection::finishHandshake(sd_bus* bus)
SDBUS_THROW_ERROR_IF(r < 0, "Failed to flush bus on opening", -r); SDBUS_THROW_ERROR_IF(r < 0, "Failed to flush bus on opening", -r);
} }
void Connection::notifyEventLoopToExit() void Connection::notifyEventLoop(int fd) const
{ {
assert(loopExitFd_.fd >= 0); assert(fd >= 0);
uint64_t value = 1; uint64_t value = 1;
auto r = write(loopExitFd_.fd, &value, sizeof(value)); auto r = write(fd, &value, sizeof(value));
SDBUS_THROW_ERROR_IF(r < 0, "Failed to notify event loop", -errno); SDBUS_THROW_ERROR_IF(r < 0, "Failed to notify event loop", -errno);
} }
void Connection::clearExitNotification() void Connection::notifyEventLoopToExit() const
{
notifyEventLoop(loopExitFd_.fd);
}
void Connection::notifyEventLoop() const
{
notifyEventLoop(eventFd_.fd);
}
void Connection::notifyEventLoopNewTimeout() const
{
// The extra notifications for new timeouts are only needed if calls are made asynchronously to the event loop.
// Are we in the same thread as the event loop? Note that it's ok to fail this check because the event loop isn't yet started.
if (loopThreadId_.load(std::memory_order_relaxed) == std::this_thread::get_id())
return;
// Get the new timeout from sd-bus
auto sdbusPollData = getEventLoopPollData();
if (sdbusPollData.timeout_usec < activeTimeout_.load(std::memory_order_relaxed))
notifyEventLoop(eventFd_.fd);
}
void Connection::clearEventLoopNotification(int fd) const
{ {
uint64_t value{}; uint64_t value{};
auto r = read(loopExitFd_.fd, &value, sizeof(value)); auto r = read(fd, &value, sizeof(value));
SDBUS_THROW_ERROR_IF(r < 0, "Failed to read from the event descriptor", -errno); SDBUS_THROW_ERROR_IF(r < 0, "Failed to read from the event descriptor", -errno);
} }
@ -393,16 +494,19 @@ bool Connection::processPendingRequest()
bool Connection::waitForNextRequest() bool Connection::waitForNextRequest()
{ {
auto bus = bus_.get(); assert(bus_ != nullptr);
assert(eventFd_.fd >= 0);
assert(bus != nullptr);
assert(loopExitFd_.fd != 0);
auto sdbusPollData = getEventLoopPollData(); auto sdbusPollData = getEventLoopPollData();
struct pollfd fds[] = {{sdbusPollData.fd, sdbusPollData.events, 0}, {loopExitFd_.fd, POLLIN, 0}}; struct pollfd fds[] = {
{sdbusPollData.fd, sdbusPollData.events, 0},
{eventFd_.fd, POLLIN, 0},
{loopExitFd_.fd, POLLIN, 0}
};
auto fdsCount = sizeof(fds)/sizeof(fds[0]); auto fdsCount = sizeof(fds)/sizeof(fds[0]);
auto timeout = sdbusPollData.timeout_usec == (uint64_t) -1 ? (uint64_t)-1 : (sdbusPollData.timeout_usec+999)/1000; auto timeout = sdbusPollData.getPollTimeout();
activeTimeout_.store(sdbusPollData.timeout_usec, std::memory_order_relaxed);
auto r = poll(fds, fdsCount, timeout); auto r = poll(fds, fdsCount, timeout);
if (r < 0 && errno == EINTR) if (r < 0 && errno == EINTR)
@ -410,22 +514,30 @@ bool Connection::waitForNextRequest()
SDBUS_THROW_ERROR_IF(r < 0, "Failed to wait on the bus", -errno); SDBUS_THROW_ERROR_IF(r < 0, "Failed to wait on the bus", -errno);
// new timeout notification
if (fds[1].revents & POLLIN) if (fds[1].revents & POLLIN)
{ {
clearExitNotification(); clearEventLoopNotification(fds[1].fd);
}
// loop exit notification
if (fds[2].revents & POLLIN)
{
clearEventLoopNotification(fds[2].fd);
return false; return false;
} }
return true; return true;
} }
std::string Connection::composeSignalMatchFilter( const std::string& objectPath std::string Connection::composeSignalMatchFilter( const std::string &sender
, const std::string& interfaceName , const std::string &objectPath
, const std::string& signalName ) , const std::string &interfaceName
, const std::string &signalName )
{ {
std::string filter; std::string filter;
filter += "type='signal',"; filter += "type='signal',";
filter += "sender='" + sender + "',";
filter += "interface='" + interfaceName + "',"; filter += "interface='" + interfaceName + "',";
filter += "member='" + signalName + "',"; filter += "member='" + signalName + "',";
filter += "path='" + objectPath + "'"; filter += "path='" + objectPath + "'";
@ -442,20 +554,49 @@ std::vector</*const */char*> Connection::to_strv(const std::vector<std::string>&
return strv; return strv;
} }
Connection::LoopExitEventFd::LoopExitEventFd() Connection::EventFd::EventFd()
{ {
fd = eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC | EFD_NONBLOCK); fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
SDBUS_THROW_ERROR_IF(fd < 0, "Failed to create event object", -errno); SDBUS_THROW_ERROR_IF(fd < 0, "Failed to create event object", -errno);
} }
Connection::LoopExitEventFd::~LoopExitEventFd() Connection::EventFd::~EventFd()
{ {
assert(fd >= 0); assert(fd >= 0);
close(fd); close(fd);
} }
} // namespace sdbus::internal
namespace sdbus {
std::optional<std::chrono::microseconds> IConnection::PollData::getRelativeTimeout() const
{
constexpr auto zero = std::chrono::microseconds::zero();
if (timeout_usec == 0)
return zero;
else if (timeout_usec == UINT64_MAX)
return std::nullopt;
// We need C so that we use the same clock as the underlying sd-bus lib.
// We use POSIX's clock_gettime in favour of std::chrono::steady_clock to ensure this.
struct timespec ts{};
auto r = clock_gettime(CLOCK_MONOTONIC, &ts);
SDBUS_THROW_ERROR_IF(r < 0, "clock_gettime failed: ", -errno);
auto now = std::chrono::nanoseconds(ts.tv_nsec) + std::chrono::seconds(ts.tv_sec);
auto absTimeout = std::chrono::microseconds(timeout_usec);
auto result = std::chrono::duration_cast<std::chrono::microseconds>(absTimeout - now);
return std::max(result, zero);
} }
int IConnection::PollData::getPollTimeout() const
{
auto timeout = getRelativeTimeout();
return timeout ? static_cast<int>(std::chrono::ceil<std::chrono::milliseconds>(timeout.value()).count()) : -1;
}
} // namespace sdbus
namespace sdbus::internal { namespace sdbus::internal {
std::unique_ptr<sdbus::internal::IConnection> createConnection() std::unique_ptr<sdbus::internal::IConnection> createConnection()
@ -466,10 +607,18 @@ std::unique_ptr<sdbus::internal::IConnection> createConnection()
return std::unique_ptr<sdbus::internal::IConnection>(connectionInternal); return std::unique_ptr<sdbus::internal::IConnection>(connectionInternal);
} }
std::unique_ptr<sdbus::internal::IConnection> createPseudoConnection()
{
auto interface = std::make_unique<sdbus::internal::SdBus>();
return std::make_unique<sdbus::internal::Connection>(std::move(interface), Connection::pseudo_bus);
} }
} // namespace sdbus::internal
namespace sdbus { namespace sdbus {
using internal::Connection;
std::unique_ptr<sdbus::IConnection> createConnection() std::unique_ptr<sdbus::IConnection> createConnection()
{ {
return createSystemBusConnection(); return createSystemBusConnection();
@ -480,12 +629,23 @@ std::unique_ptr<sdbus::IConnection> createConnection(const std::string& name)
return createSystemBusConnection(name); return createSystemBusConnection(name);
} }
std::unique_ptr<sdbus::IConnection> createDefaultBusConnection()
{
auto interface = std::make_unique<sdbus::internal::SdBus>();
return std::make_unique<sdbus::internal::Connection>(std::move(interface), Connection::default_bus);
}
std::unique_ptr<sdbus::IConnection> createDefaultBusConnection(const std::string& name)
{
auto conn = createDefaultBusConnection();
conn->requestName(name);
return conn;
}
std::unique_ptr<sdbus::IConnection> createSystemBusConnection() std::unique_ptr<sdbus::IConnection> createSystemBusConnection()
{ {
auto interface = std::make_unique<sdbus::internal::SdBus>(); auto interface = std::make_unique<sdbus::internal::SdBus>();
assert(interface != nullptr); return std::make_unique<sdbus::internal::Connection>(std::move(interface), Connection::system_bus);
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) std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name)
@ -498,9 +658,7 @@ std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string&
std::unique_ptr<sdbus::IConnection> createSessionBusConnection() std::unique_ptr<sdbus::IConnection> createSessionBusConnection()
{ {
auto interface = std::make_unique<sdbus::internal::SdBus>(); auto interface = std::make_unique<sdbus::internal::SdBus>();
assert(interface != nullptr); return std::make_unique<sdbus::internal::Connection>(std::move(interface), Connection::session_bus);
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) std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name)
@ -510,12 +668,17 @@ std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string
return conn; return conn;
} }
std::unique_ptr<sdbus::IConnection> createRemoteSystemBusConnection(const std::string& host) std::unique_ptr<sdbus::IConnection> createSessionBusConnectionWithAddress(const std::string &address)
{ {
auto interface = std::make_unique<sdbus::internal::SdBus>(); auto interface = std::make_unique<sdbus::internal::SdBus>();
assert(interface != nullptr); assert(interface != nullptr);
constexpr sdbus::internal::Connection::remote_system_bus_t remote_system_bus; return std::make_unique<sdbus::internal::Connection>(std::move(interface), Connection::custom_session_bus, address);
return std::make_unique<sdbus::internal::Connection>(std::move(interface), remote_system_bus, host);
} }
std::unique_ptr<sdbus::IConnection> createRemoteSystemBusConnection(const std::string& host)
{
auto interface = std::make_unique<sdbus::internal::SdBus>();
return std::make_unique<sdbus::internal::Connection>(std::move(interface), Connection::remote_system_bus, host);
} }
} // namespace sdbus

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file Connection.h * @file Connection.h
* *
@ -43,18 +43,29 @@
namespace sdbus::internal { namespace sdbus::internal {
class Connection final class Connection final
: public sdbus::IConnection // External, public interface : public sdbus::internal::IConnection
, public sdbus::internal::IConnection // Internal, private interface
{ {
public: public:
// Bus type tags // Bus type tags
struct default_bus_t{};
inline static constexpr default_bus_t default_bus{};
struct system_bus_t{}; struct system_bus_t{};
inline static constexpr system_bus_t system_bus{};
struct session_bus_t{}; struct session_bus_t{};
inline static constexpr session_bus_t session_bus{};
struct custom_session_bus_t{};
inline static constexpr custom_session_bus_t custom_session_bus{};
struct remote_system_bus_t{}; struct remote_system_bus_t{};
inline static constexpr remote_system_bus_t remote_system_bus{};
struct pseudo_bus_t{}; // A bus connection that is not really established with D-Bus daemon
inline static constexpr pseudo_bus_t pseudo_bus{};
Connection(std::unique_ptr<ISdBus>&& interface, default_bus_t);
Connection(std::unique_ptr<ISdBus>&& interface, system_bus_t); Connection(std::unique_ptr<ISdBus>&& interface, system_bus_t);
Connection(std::unique_ptr<ISdBus>&& interface, session_bus_t); Connection(std::unique_ptr<ISdBus>&& interface, session_bus_t);
Connection(std::unique_ptr<ISdBus>&& interface, custom_session_bus_t, const std::string& address);
Connection(std::unique_ptr<ISdBus>&& interface, remote_system_bus_t, const std::string& host); Connection(std::unique_ptr<ISdBus>&& interface, remote_system_bus_t, const std::string& host);
Connection(std::unique_ptr<ISdBus>&& interface, pseudo_bus_t);
~Connection() override; ~Connection() override;
void requestName(const std::string& name) override; void requestName(const std::string& name) override;
@ -67,18 +78,22 @@ namespace sdbus::internal {
bool processPendingRequest() override; bool processPendingRequest() override;
void addObjectManager(const std::string& objectPath) override; void addObjectManager(const std::string& objectPath) override;
SlotPtr addObjectManager(const std::string& objectPath, void* /*dummy*/) override; void addObjectManager(const std::string& objectPath, floating_slot_t) override;
Slot addObjectManager(const std::string& objectPath, request_slot_t) override;
void setMethodCallTimeout(uint64_t timeout) override; void setMethodCallTimeout(uint64_t timeout) override;
uint64_t getMethodCallTimeout() const override; uint64_t getMethodCallTimeout() const override;
[[nodiscard]] Slot addMatch(const std::string& match, message_handler callback) override;
void addMatch(const std::string& match, message_handler callback, floating_slot_t) override;
const ISdBus& getSdBusInterface() const override; const ISdBus& getSdBusInterface() const override;
ISdBus& getSdBusInterface() override; ISdBus& getSdBusInterface() override;
SlotPtr addObjectVTable( const std::string& objectPath Slot addObjectVTable( const std::string& objectPath
, const std::string& interfaceName , const std::string& interfaceName
, const sd_bus_vtable* vtable , const sd_bus_vtable* vtable
, void* userData ) override; , void* userData ) override;
PlainMessage createPlainMessage() const override; PlainMessage createPlainMessage() const override;
MethodCall createMethodCall( const std::string& destination MethodCall createMethodCall( const std::string& destination
@ -99,11 +114,12 @@ namespace sdbus::internal {
void emitInterfacesRemovedSignal( const std::string& objectPath void emitInterfacesRemovedSignal( const std::string& objectPath
, const std::vector<std::string>& interfaces ) override; , const std::vector<std::string>& interfaces ) override;
SlotPtr registerSignalHandler( const std::string& objectPath Slot registerSignalHandler( const std::string& sender
, const std::string& interfaceName , const std::string& objectPath
, const std::string& signalName , const std::string& interfaceName
, sd_bus_message_handler_t callback , const std::string& signalName
, void* userData ) override; , sd_bus_message_handler_t callback
, void* userData ) override;
MethodReply tryCallMethodSynchronously(const MethodCall& message, uint64_t timeout) override; MethodReply tryCallMethodSynchronously(const MethodCall& message, uint64_t timeout) override;
@ -113,21 +129,35 @@ namespace sdbus::internal {
Connection(std::unique_ptr<ISdBus>&& interface, const BusFactory& busFactory); Connection(std::unique_ptr<ISdBus>&& interface, const BusFactory& busFactory);
BusPtr openBus(const std::function<int(sd_bus**)>& busFactory); BusPtr openBus(const std::function<int(sd_bus**)>& busFactory);
BusPtr openPseudoBus();
void finishHandshake(sd_bus* bus); void finishHandshake(sd_bus* bus);
bool waitForNextRequest(); bool waitForNextRequest();
static std::string composeSignalMatchFilter( const std::string& objectPath static std::string composeSignalMatchFilter( const std::string &sender
, const std::string& interfaceName , const std::string &objectPath
, const std::string& signalName ); , const std::string &interfaceName
void notifyEventLoopToExit(); , const std::string &signalName);
void clearExitNotification(); void notifyEventLoop(int fd) const;
void notifyEventLoopToExit() const;
void clearEventLoopNotification(int fd) const;
void notifyEventLoop() const override;
void notifyEventLoopNewTimeout() const override;
private:
void joinWithEventLoop(); void joinWithEventLoop();
static std::vector</*const */char*> to_strv(const std::vector<std::string>& strings); static std::vector</*const */char*> to_strv(const std::vector<std::string>& strings);
struct LoopExitEventFd struct EventFd
{ {
LoopExitEventFd(); EventFd();
~LoopExitEventFd(); ~EventFd();
int fd; int fd{-1};
};
struct MatchInfo
{
message_handler callback;
Connection& connection;
sd_bus_slot *slot;
}; };
private: private:
@ -136,7 +166,10 @@ namespace sdbus::internal {
std::thread asyncLoopThread_; std::thread asyncLoopThread_;
std::atomic<std::thread::id> loopThreadId_; std::atomic<std::thread::id> loopThreadId_;
std::mutex loopMutex_; std::mutex loopMutex_;
LoopExitEventFd loopExitFd_; EventFd loopExitFd_;
EventFd eventFd_;
std::atomic<uint64_t> activeTimeout_{};
std::vector<Slot> floatingMatchRules_;
}; };
} }

View File

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

View File

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

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file IConnection.h * @file IConnection.h
* *
@ -27,6 +27,7 @@
#ifndef SDBUS_CXX_INTERNAL_ICONNECTION_H_ #ifndef SDBUS_CXX_INTERNAL_ICONNECTION_H_
#define SDBUS_CXX_INTERNAL_ICONNECTION_H_ #define SDBUS_CXX_INTERNAL_ICONNECTION_H_
#include <sdbus-c++/IConnection.h>
#include <systemd/sd-bus.h> #include <systemd/sd-bus.h>
#include <string> #include <string>
#include <memory> #include <memory>
@ -46,20 +47,19 @@ namespace sdbus {
namespace sdbus::internal { namespace sdbus::internal {
using SlotPtr = std::unique_ptr<void, std::function<void(void*)>>;
class IConnection class IConnection
: public ::sdbus::IConnection
{ {
public: public:
virtual ~IConnection() = default; ~IConnection() override = default;
virtual const ISdBus& getSdBusInterface() const = 0; virtual const ISdBus& getSdBusInterface() const = 0;
virtual ISdBus& getSdBusInterface() = 0; virtual ISdBus& getSdBusInterface() = 0;
[[nodiscard]] virtual SlotPtr addObjectVTable( const std::string& objectPath [[nodiscard]] virtual Slot addObjectVTable( const std::string& objectPath
, const std::string& interfaceName , const std::string& interfaceName
, const sd_bus_vtable* vtable , const sd_bus_vtable* vtable
, void* userData ) = 0; , void* userData ) = 0;
virtual PlainMessage createPlainMessage() const = 0; virtual PlainMessage createPlainMessage() const = 0;
virtual MethodCall createMethodCall( const std::string& destination virtual MethodCall createMethodCall( const std::string& destination
@ -80,21 +80,23 @@ namespace sdbus::internal {
virtual void emitInterfacesRemovedSignal( const std::string& objectPath virtual void emitInterfacesRemovedSignal( const std::string& objectPath
, const std::vector<std::string>& interfaces ) = 0; , const std::vector<std::string>& interfaces ) = 0;
[[nodiscard]] virtual SlotPtr addObjectManager(const std::string& objectPath, void* /*dummy*/ = nullptr) = 0; using sdbus::IConnection::addObjectManager;
[[nodiscard]] virtual Slot addObjectManager(const std::string& objectPath, request_slot_t) = 0;
[[nodiscard]] virtual SlotPtr registerSignalHandler( const std::string& objectPath [[nodiscard]] virtual Slot registerSignalHandler( const std::string& sender
, const std::string& interfaceName , const std::string& objectPath
, const std::string& signalName , const std::string& interfaceName
, sd_bus_message_handler_t callback , const std::string& signalName
, void* userData ) = 0; , sd_bus_message_handler_t callback
, void* userData ) = 0;
virtual void enterEventLoopAsync() = 0;
virtual void leaveEventLoop() = 0;
virtual void notifyEventLoop() const = 0;
virtual void notifyEventLoopNewTimeout() const = 0;
virtual MethodReply tryCallMethodSynchronously(const MethodCall& message, uint64_t timeout) = 0; virtual MethodReply tryCallMethodSynchronously(const MethodCall& message, uint64_t timeout) = 0;
}; };
[[nodiscard]] std::unique_ptr<sdbus::internal::IConnection> createConnection(); [[nodiscard]] std::unique_ptr<sdbus::internal::IConnection> createConnection();
[[nodiscard]] std::unique_ptr<sdbus::internal::IConnection> createPseudoConnection();
} }

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file ISdBus.h * @file ISdBus.h
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru) * @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@ -66,8 +66,10 @@ namespace sdbus::internal {
virtual int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) = 0; virtual int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) = 0;
virtual int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) = 0; virtual int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) = 0;
virtual int sd_bus_open_user(sd_bus **ret) = 0; virtual int sd_bus_open(sd_bus **ret) = 0;
virtual int sd_bus_open_system(sd_bus **ret) = 0; virtual int sd_bus_open_system(sd_bus **ret) = 0;
virtual int sd_bus_open_user(sd_bus **ret) = 0;
virtual int sd_bus_open_user_with_address(sd_bus **ret, const char* address) = 0;
virtual int sd_bus_open_system_remote(sd_bus **ret, const char* host) = 0; virtual int sd_bus_open_system_remote(sd_bus **ret, const char* host) = 0;
virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) = 0; virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) = 0;
virtual int sd_bus_release_name(sd_bus *bus, const char *name) = 0; virtual int sd_bus_release_name(sd_bus *bus, const char *name) = 0;
@ -77,11 +79,28 @@ namespace sdbus::internal {
virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) = 0; virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) = 0;
virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) = 0; virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) = 0;
virtual int sd_bus_new(sd_bus **ret) = 0;
virtual int sd_bus_start(sd_bus *bus) = 0;
virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) = 0; virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) = 0;
virtual int sd_bus_get_poll_data(sd_bus *bus, PollData* data) = 0; virtual int sd_bus_get_poll_data(sd_bus *bus, PollData* data) = 0;
virtual int sd_bus_flush(sd_bus *bus) = 0; virtual int sd_bus_flush(sd_bus *bus) = 0;
virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) = 0; virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) = 0;
virtual sd_bus *sd_bus_close_unref(sd_bus *bus) = 0;
virtual int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) = 0;
virtual int sd_bus_query_sender_creds(sd_bus_message *m, uint64_t mask, sd_bus_creds **c) = 0;
virtual sd_bus_creds* sd_bus_creds_unref(sd_bus_creds *c) = 0;
virtual int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) = 0;
virtual int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid) = 0;
virtual int sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *uid) = 0;
virtual int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid) = 0;
virtual int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid) = 0;
virtual int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids) = 0;
virtual int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **label) = 0;
}; };
} }

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file Message.cpp * @file Message.cpp
* *
@ -583,12 +583,14 @@ void Message::rewind(bool complete)
std::string Message::getInterfaceName() const std::string Message::getInterfaceName() const
{ {
return sd_bus_message_get_interface((sd_bus_message*)msg_); auto interface = sd_bus_message_get_interface((sd_bus_message*)msg_);
return interface != nullptr ? interface : "";
} }
std::string Message::getMemberName() const std::string Message::getMemberName() const
{ {
return sd_bus_message_get_member((sd_bus_message*)msg_); auto member = sd_bus_message_get_member((sd_bus_message*)msg_);
return member != nullptr ? member : "";
} }
std::string Message::getSender() const std::string Message::getSender() const
@ -596,6 +598,18 @@ std::string Message::getSender() const
return sd_bus_message_get_sender((sd_bus_message*)msg_); return sd_bus_message_get_sender((sd_bus_message*)msg_);
} }
std::string Message::getPath() const
{
auto path = sd_bus_message_get_path((sd_bus_message*)msg_);
return path != nullptr ? path : "";
}
std::string Message::getDestination() const
{
auto destination = sd_bus_message_get_destination((sd_bus_message*)msg_);
return destination != nullptr ? destination : "";
}
void Message::peekType(std::string& type, std::string& contents) const void Message::peekType(std::string& type, std::string& contents) const
{ {
char typeSig; char typeSig;
@ -603,7 +617,7 @@ void Message::peekType(std::string& type, std::string& contents) const
auto r = sd_bus_message_peek_type((sd_bus_message*)msg_, &typeSig, &contentsSig); auto r = sd_bus_message_peek_type((sd_bus_message*)msg_, &typeSig, &contentsSig);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to peek message type", -r); SDBUS_THROW_ERROR_IF(r < 0, "Failed to peek message type", -r);
type = typeSig; type = typeSig;
contents = contentsSig; contents = contentsSig ? contentsSig : "";
} }
bool Message::isValid() const bool Message::isValid() const
@ -616,6 +630,124 @@ bool Message::isEmpty() const
return sd_bus_message_is_empty((sd_bus_message*)msg_) != 0; return sd_bus_message_is_empty((sd_bus_message*)msg_) != 0;
} }
pid_t Message::getCredsPid() const
{
uint64_t mask = SD_BUS_CREDS_PID | SD_BUS_CREDS_AUGMENT;
sd_bus_creds *creds = nullptr;
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
pid_t pid = 0;
r = sdbus_->sd_bus_creds_get_pid(creds, &pid);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred pid", -r);
return pid;
}
uid_t Message::getCredsUid() const
{
uint64_t mask = SD_BUS_CREDS_UID | SD_BUS_CREDS_AUGMENT;
sd_bus_creds *creds = nullptr;
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
uid_t uid = (uid_t)-1;
r = sdbus_->sd_bus_creds_get_uid(creds, &uid);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred uid", -r);
return uid;
}
uid_t Message::getCredsEuid() const
{
uint64_t mask = SD_BUS_CREDS_EUID | SD_BUS_CREDS_AUGMENT;
sd_bus_creds *creds = nullptr;
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
uid_t euid = (uid_t)-1;
r = sdbus_->sd_bus_creds_get_euid(creds, &euid);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred euid", -r);
return euid;
}
gid_t Message::getCredsGid() const
{
uint64_t mask = SD_BUS_CREDS_GID | SD_BUS_CREDS_AUGMENT;
sd_bus_creds *creds = nullptr;
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
gid_t gid = (gid_t)-1;
r = sdbus_->sd_bus_creds_get_gid(creds, &gid);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred gid", -r);
return gid;
}
gid_t Message::getCredsEgid() const
{
uint64_t mask = SD_BUS_CREDS_EGID | SD_BUS_CREDS_AUGMENT;
sd_bus_creds *creds = nullptr;
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
gid_t egid = (gid_t)-1;
r = sdbus_->sd_bus_creds_get_egid(creds, &egid);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred egid", -r);
return egid;
}
std::vector<gid_t> Message::getCredsSupplementaryGids() const
{
uint64_t mask = SD_BUS_CREDS_SUPPLEMENTARY_GIDS | SD_BUS_CREDS_AUGMENT;
sd_bus_creds *creds = nullptr;
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
const gid_t *cGids = nullptr;
r = sdbus_->sd_bus_creds_get_supplementary_gids(creds, &cGids);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred supplementary gids", -r);
std::vector<gid_t> gids{};
if (cGids != nullptr)
{
for (int i = 0; i < r; i++)
gids.push_back(cGids[i]);
}
return gids;
}
std::string Message::getSELinuxContext() const
{
uint64_t mask = SD_BUS_CREDS_AUGMENT | SD_BUS_CREDS_SELINUX_CONTEXT;
sd_bus_creds *creds = nullptr;
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
const char *cLabel = nullptr;
r = sdbus_->sd_bus_creds_get_selinux_context(creds, &cLabel);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred selinux context", -r);
return cLabel;
}
MethodCall::MethodCall( void *msg
, internal::ISdBus *sdbus
, const internal::IConnection *connection
, adopt_message_t) noexcept
: Message(msg, sdbus, adopt_message)
, connection_(connection)
{
assert(connection_ != nullptr);
}
void MethodCall::dontExpectReply() void MethodCall::dontExpectReply()
{ {
auto r = sd_bus_message_set_expect_reply((sd_bus_message*)msg_, 0); auto r = sd_bus_message_set_expect_reply((sd_bus_message*)msg_, 0);
@ -650,6 +782,10 @@ MethodReply MethodCall::sendWithReply(uint64_t timeout) const
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method", -r); SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method", -r);
// Force event loop to re-enter processing to handle queued messages
SDBUS_THROW_ERROR_IF(connection_ == nullptr, "Invalid use of MethodCall API", ENOTSUP);
connection_->notifyEventLoop();
return Factory::create<MethodReply>(sdbusReply, sdbus_, adopt_message); return Factory::create<MethodReply>(sdbusReply, sdbus_, adopt_message);
} }
@ -658,23 +794,40 @@ MethodReply MethodCall::sendWithNoReply() const
auto r = sdbus_->sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr); 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); SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method with no reply", -r);
// Force event loop to re-enter processing to handle queued messages
SDBUS_THROW_ERROR_IF(connection_ == nullptr, "Invalid use of MethodCall API", ENOTSUP);
connection_->notifyEventLoop();
return Factory::create<MethodReply>(); // No reply return Factory::create<MethodReply>(); // No reply
} }
void MethodCall::send(void* callback, void* userData, uint64_t timeout, dont_request_slot_t) const void MethodCall::send(void* callback, void* userData, uint64_t timeout, dont_request_slot_t) const
{ {
auto r = sdbus_->sd_bus_call_async(nullptr, nullptr, (sd_bus_message*)msg_, (sd_bus_message_handler_t)callback, userData, timeout); MethodCall::send(callback, userData, timeout, floating_slot);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method", -r);
} }
MethodCall::Slot MethodCall::send(void* callback, void* userData, uint64_t timeout) const void MethodCall::send(void* callback, void* userData, uint64_t timeout, floating_slot_t) const
{
auto r = sdbus_->sd_bus_call_async(nullptr, nullptr, (sd_bus_message*)msg_, (sd_bus_message_handler_t)callback, userData, timeout);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method", -r);
// Force event loop to re-enter polling with the async call timeout if that is less than the one used in current poll
SDBUS_THROW_ERROR_IF(connection_ == nullptr, "Invalid use of MethodCall API", ENOTSUP);
connection_->notifyEventLoopNewTimeout();
}
Slot MethodCall::send(void* callback, void* userData, uint64_t timeout) const
{ {
sd_bus_slot* slot; sd_bus_slot* slot;
auto r = sdbus_->sd_bus_call_async(nullptr, &slot, (sd_bus_message*)msg_, (sd_bus_message_handler_t)callback, userData, timeout); 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); 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); }}; // Force event loop to re-enter polling with the async call timeout if that is less than the one used in current poll
SDBUS_THROW_ERROR_IF(connection_ == nullptr, "Invalid use of MethodCall API", ENOTSUP);
connection_->notifyEventLoopNewTimeout();
return {slot, [sdbus_ = sdbus_](void *slot){ sdbus_->sd_bus_slot_unref((sd_bus_slot*)slot); }};
} }
MethodReply MethodCall::createReply() const MethodReply MethodCall::createReply() const
@ -711,9 +864,20 @@ void Signal::send() const
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit signal", -r); SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit signal", -r);
} }
void Signal::setDestination(const std::string& destination)
{
auto r = sdbus_->sd_bus_message_set_destination((sd_bus_message*)msg_, destination.c_str());
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set signal destination", -r);
}
PlainMessage createPlainMessage() PlainMessage createPlainMessage()
{ {
static auto connection = internal::createConnection(); //static auto connection = internal::createConnection();
// Let's create a pseudo connection -- one that does not really connect to the real bus.
// This is a bit of a hack, but it enables use to work with D-Bus message locally without
// the need of D-Bus daemon. This is especially useful in unit tests of both sdbus-c++ and client code.
// Additionally, it's light-weight and fast solution.
static auto connection = internal::createPseudoConnection();
return connection->createPlainMessage(); return connection->createPlainMessage();
} }

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file MessageUtils.h * @file MessageUtils.h
* *
@ -57,6 +57,12 @@ namespace sdbus
{ {
return _Msg{msg, sdbus, adopt_message}; return _Msg{msg, sdbus, adopt_message};
} }
template<typename _Msg>
static _Msg create(void *msg, internal::ISdBus* sdbus, const internal::IConnection* connection, adopt_message_t)
{
return _Msg{msg, sdbus, connection, adopt_message};
}
}; };
PlainMessage createPlainMessage(); PlainMessage createPlainMessage();

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file Object.cpp * @file Object.cpp
* *
@ -31,7 +31,9 @@
#include <sdbus-c++/Error.h> #include <sdbus-c++/Error.h>
#include <sdbus-c++/MethodResult.h> #include <sdbus-c++/MethodResult.h>
#include <sdbus-c++/Flags.h> #include <sdbus-c++/Flags.h>
#include "ScopeGuard.h"
#include "IConnection.h" #include "IConnection.h"
#include "Utils.h"
#include "VTableUtils.h" #include "VTableUtils.h"
#include <systemd/sd-bus.h> #include <systemd/sd-bus.h>
#include <utility> #include <utility>
@ -42,6 +44,7 @@ namespace sdbus::internal {
Object::Object(sdbus::internal::IConnection& connection, std::string objectPath) Object::Object(sdbus::internal::IConnection& connection, std::string objectPath)
: connection_(connection), objectPath_(std::move(objectPath)) : connection_(connection), objectPath_(std::move(objectPath))
{ {
SDBUS_CHECK_OBJECT_PATH(objectPath_);
} }
void Object::registerMethod( const std::string& interfaceName void Object::registerMethod( const std::string& interfaceName
@ -70,9 +73,11 @@ void Object::registerMethod( const std::string& interfaceName
, method_callback methodCallback , method_callback methodCallback
, Flags flags ) , Flags flags )
{ {
SDBUS_CHECK_INTERFACE_NAME(interfaceName);
SDBUS_CHECK_MEMBER_NAME(methodName);
SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL); SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL);
auto& interface = interfaces_[interfaceName]; auto& interface = getInterface(interfaceName);
InterfaceData::MethodData methodData{ std::move(inputSignature) InterfaceData::MethodData methodData{ std::move(inputSignature)
, std::move(outputSignature) , std::move(outputSignature)
, paramNamesToString(inputNames) + paramNamesToString(outputNames) , paramNamesToString(inputNames) + paramNamesToString(outputNames)
@ -97,7 +102,10 @@ void Object::registerSignal( const std::string& interfaceName
, const std::vector<std::string>& paramNames , const std::vector<std::string>& paramNames
, Flags flags ) , Flags flags )
{ {
auto& interface = interfaces_[interfaceName]; SDBUS_CHECK_INTERFACE_NAME(interfaceName);
SDBUS_CHECK_MEMBER_NAME(signalName);
auto& interface = getInterface(interfaceName);
InterfaceData::SignalData signalData{std::move(signature), paramNamesToString(paramNames), std::move(flags)}; InterfaceData::SignalData signalData{std::move(signature), paramNamesToString(paramNames), std::move(flags)};
auto inserted = interface.signals.emplace(std::move(signalName), std::move(signalData)).second; auto inserted = interface.signals.emplace(std::move(signalName), std::move(signalData)).second;
@ -126,14 +134,16 @@ void Object::registerProperty( const std::string& interfaceName
, property_set_callback setCallback , property_set_callback setCallback
, Flags flags ) , Flags flags )
{ {
SDBUS_CHECK_INTERFACE_NAME(interfaceName);
SDBUS_CHECK_MEMBER_NAME(propertyName);
SDBUS_THROW_ERROR_IF(!getCallback && !setCallback, "Invalid property callbacks provided", EINVAL); SDBUS_THROW_ERROR_IF(!getCallback && !setCallback, "Invalid property callbacks provided", EINVAL);
auto& interface = interfaces_[interfaceName]; auto& interface = getInterface(interfaceName);
InterfaceData::PropertyData propertyData{ std::move(signature) InterfaceData::PropertyData propertyData{ std::move(signature)
, std::move(getCallback) , std::move(getCallback)
, std::move(setCallback) , std::move(setCallback)
, std::move(flags)}; , std::move(flags) };
auto inserted = interface.properties.emplace(std::move(propertyName), std::move(propertyData)).second; auto inserted = interface.properties.emplace(std::move(propertyName), std::move(propertyData)).second;
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register property: property already exists", EINVAL); SDBUS_THROW_ERROR_IF(!inserted, "Failed to register property: property already exists", EINVAL);
@ -141,7 +151,7 @@ void Object::registerProperty( const std::string& interfaceName
void Object::setInterfaceFlags(const std::string& interfaceName, Flags flags) void Object::setInterfaceFlags(const std::string& interfaceName, Flags flags)
{ {
auto& interface = interfaces_[interfaceName]; auto& interface = getInterface(interfaceName);
interface.flags = flags; interface.flags = flags;
} }
@ -207,7 +217,7 @@ void Object::emitInterfacesRemovedSignal(const std::vector<std::string>& interfa
void Object::addObjectManager() void Object::addObjectManager()
{ {
objectManagerSlot_ = connection_.addObjectManager(objectPath_); objectManagerSlot_ = connection_.addObjectManager(objectPath_, request_slot);
} }
void Object::removeObjectManager() void Object::removeObjectManager()
@ -222,7 +232,7 @@ bool Object::hasObjectManager() const
sdbus::IConnection& Object::getConnection() const sdbus::IConnection& Object::getConnection() const
{ {
return dynamic_cast<sdbus::IConnection&>(connection_); return connection_;
} }
const std::string& Object::getObjectPath() const const std::string& Object::getObjectPath() const
@ -230,6 +240,16 @@ const std::string& Object::getObjectPath() const
return objectPath_; return objectPath_;
} }
const Message* Object::getCurrentlyProcessedMessage() const
{
return m_CurrentlyProcessedMessage.load(std::memory_order_relaxed);
}
Object::InterfaceData& Object::getInterface(const std::string& interfaceName)
{
return interfaces_.emplace(interfaceName, *this).first->second;
}
const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData) const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData)
{ {
auto& vtable = interfaceData.vtable; auto& vtable = interfaceData.vtable;
@ -256,7 +276,7 @@ void Object::registerMethodsToVTable(const InterfaceData& interfaceData, std::ve
, methodData.outputArgs.c_str() , methodData.outputArgs.c_str()
, methodData.paramNames.c_str() , methodData.paramNames.c_str()
, &Object::sdbus_method_callback , &Object::sdbus_method_callback
, methodData.flags_.toSdBusMethodFlags() )); , methodData.flags.toSdBusMethodFlags() ));
} }
} }
@ -299,7 +319,7 @@ void Object::activateInterfaceVTable( const std::string& interfaceName
, InterfaceData& interfaceData , InterfaceData& interfaceData
, const std::vector<sd_bus_vtable>& vtable ) , const std::vector<sd_bus_vtable>& vtable )
{ {
interfaceData.slot = connection_.addObjectVTable(objectPath_, interfaceName, &vtable[0], this); interfaceData.slot = connection_.addObjectVTable(objectPath_, interfaceName, &vtable[0], &interfaceData);
} }
std::string Object::paramNamesToString(const std::vector<std::string>& paramNames) std::string Object::paramNamesToString(const std::vector<std::string>& paramNames)
@ -312,20 +332,26 @@ std::string Object::paramNamesToString(const std::vector<std::string>& paramName
int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError) int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
{ {
auto* object = static_cast<Object*>(userData); auto* interfaceData = static_cast<InterfaceData*>(userData);
assert(object != nullptr); assert(interfaceData != nullptr);
auto& object = interfaceData->object;
auto message = Message::Factory::create<MethodCall>(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 object.m_CurrentlyProcessedMessage.store(&message, std::memory_order_relaxed);
auto& callback = object->interfaces_[message.getInterfaceName()].methods[message.getMemberName()].callback; SCOPE_EXIT
{
object.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
};
auto& callback = interfaceData->methods[message.getMemberName()].callback;
assert(callback); assert(callback);
try try
{ {
callback(std::move(message)); callback(message);
} }
catch (const sdbus::Error& e) catch (const Error& e)
{ {
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str()); sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
} }
@ -335,17 +361,17 @@ int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData,
int Object::sdbus_property_get_callback( sd_bus */*bus*/ int Object::sdbus_property_get_callback( sd_bus */*bus*/
, const char */*objectPath*/ , const char */*objectPath*/
, const char *interface , const char */*interface*/
, const char *property , const char *property
, sd_bus_message *sdbusReply , sd_bus_message *sdbusReply
, void *userData , void *userData
, sd_bus_error *retError ) , sd_bus_error *retError )
{ {
auto* object = static_cast<Object*>(userData); auto* interfaceData = static_cast<InterfaceData*>(userData);
assert(object != nullptr); assert(interfaceData != nullptr);
auto& object = interfaceData->object;
// Note: The lookup can be optimized by using sorted vectors instead of associative containers auto& callback = interfaceData->properties[property].getCallback;
auto& callback = object->interfaces_[interface].properties[property].getCallback;
// Getter can be empty - the case of "write-only" property // Getter can be empty - the case of "write-only" property
if (!callback) if (!callback)
{ {
@ -353,13 +379,13 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/
return 1; return 1;
} }
auto reply = Message::Factory::create<PropertyGetReply>(sdbusReply, &object->connection_.getSdBusInterface()); auto reply = Message::Factory::create<PropertyGetReply>(sdbusReply, &object.connection_.getSdBusInterface());
try try
{ {
callback(reply); callback(reply);
} }
catch (const sdbus::Error& e) catch (const Error& e)
{ {
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str()); sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
} }
@ -369,26 +395,32 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/
int Object::sdbus_property_set_callback( sd_bus */*bus*/ int Object::sdbus_property_set_callback( sd_bus */*bus*/
, const char */*objectPath*/ , const char */*objectPath*/
, const char *interface , const char */*interface*/
, const char *property , const char *property
, sd_bus_message *sdbusValue , sd_bus_message *sdbusValue
, void *userData , void *userData
, sd_bus_error *retError ) , sd_bus_error *retError )
{ {
auto* object = static_cast<Object*>(userData); auto* interfaceData = static_cast<InterfaceData*>(userData);
assert(object != nullptr); assert(interfaceData != nullptr);
auto& object = interfaceData->object;
// Note: The lookup can be optimized by using sorted vectors instead of associative containers auto& callback = interfaceData->properties[property].setCallback;
auto& callback = object->interfaces_[interface].properties[property].setCallback;
assert(callback); assert(callback);
auto value = Message::Factory::create<PropertySetCall>(sdbusValue, &object->connection_.getSdBusInterface()); auto value = Message::Factory::create<PropertySetCall>(sdbusValue, &object.connection_.getSdBusInterface());
object.m_CurrentlyProcessedMessage.store(&value, std::memory_order_relaxed);
SCOPE_EXIT
{
object.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
};
try try
{ {
callback(value); callback(value);
} }
catch (const sdbus::Error& e) catch (const Error& e)
{ {
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str()); sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
} }

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file Object.h * @file Object.h
* *
@ -35,6 +35,7 @@
#include <vector> #include <vector>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <atomic>
#include <cassert> #include <cassert>
namespace sdbus::internal { namespace sdbus::internal {
@ -102,11 +103,14 @@ namespace sdbus::internal {
sdbus::IConnection& getConnection() const override; sdbus::IConnection& getConnection() const override;
const std::string& getObjectPath() const override; const std::string& getObjectPath() const override;
const Message* getCurrentlyProcessedMessage() const override;
private: private:
using InterfaceName = std::string; using InterfaceName = std::string;
struct InterfaceData struct InterfaceData
{ {
InterfaceData(Object& object) : object(object) {}
using MethodName = std::string; using MethodName = std::string;
struct MethodData struct MethodData
{ {
@ -114,7 +118,7 @@ namespace sdbus::internal {
const std::string outputArgs; const std::string outputArgs;
const std::string paramNames; const std::string paramNames;
method_callback callback; method_callback callback;
Flags flags_; Flags flags;
}; };
std::map<MethodName, MethodData> methods; std::map<MethodName, MethodData> methods;
using SignalName = std::string; using SignalName = std::string;
@ -136,10 +140,14 @@ namespace sdbus::internal {
std::map<PropertyName, PropertyData> properties; std::map<PropertyName, PropertyData> properties;
std::vector<sd_bus_vtable> vtable; std::vector<sd_bus_vtable> vtable;
Flags flags; Flags flags;
Object& object;
SlotPtr slot; // This is intentionally the last member, because it must be destructed first,
// releasing callbacks above before the callbacks themselves are destructed.
Slot slot;
}; };
InterfaceData& getInterface(const std::string& interfaceName);
static const std::vector<sd_bus_vtable>& createInterfaceVTable(InterfaceData& interfaceData); static const std::vector<sd_bus_vtable>& createInterfaceVTable(InterfaceData& interfaceData);
static void registerMethodsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable); static void registerMethodsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable);
static void registerSignalsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable); static void registerSignalsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable);
@ -169,7 +177,8 @@ namespace sdbus::internal {
sdbus::internal::IConnection& connection_; sdbus::internal::IConnection& connection_;
std::string objectPath_; std::string objectPath_;
std::map<InterfaceName, InterfaceData> interfaces_; std::map<InterfaceName, InterfaceData> interfaces_;
SlotPtr objectManagerSlot_; Slot objectManagerSlot_;
std::atomic<const Message*> m_CurrentlyProcessedMessage{nullptr};
}; };
} }

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file Proxy.cpp * @file Proxy.cpp
* *
@ -27,6 +27,7 @@
#include "Proxy.h" #include "Proxy.h"
#include "IConnection.h" #include "IConnection.h"
#include "MessageUtils.h" #include "MessageUtils.h"
#include "Utils.h"
#include "sdbus-c++/Message.h" #include "sdbus-c++/Message.h"
#include "sdbus-c++/IConnection.h" #include "sdbus-c++/IConnection.h"
#include "sdbus-c++/Error.h" #include "sdbus-c++/Error.h"
@ -34,7 +35,7 @@
#include <systemd/sd-bus.h> #include <systemd/sd-bus.h>
#include <cassert> #include <cassert>
#include <chrono> #include <chrono>
#include <thread> #include <utility>
namespace sdbus::internal { namespace sdbus::internal {
@ -43,6 +44,9 @@ Proxy::Proxy(sdbus::internal::IConnection& connection, std::string destination,
, destination_(std::move(destination)) , destination_(std::move(destination))
, objectPath_(std::move(objectPath)) , objectPath_(std::move(objectPath))
{ {
SDBUS_CHECK_SERVICE_NAME(destination_);
SDBUS_CHECK_OBJECT_PATH(objectPath_);
// The connection is not ours only, it is owned and managed by the user and we just reference // The connection is not ours only, it is owned and managed by the user and we just reference
// it here, so we expect the client to manage the event loop upon this connection themselves. // it here, so we expect the client to manage the event loop upon this connection themselves.
} }
@ -54,6 +58,9 @@ Proxy::Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
, destination_(std::move(destination)) , destination_(std::move(destination))
, objectPath_(std::move(objectPath)) , objectPath_(std::move(objectPath))
{ {
SDBUS_CHECK_SERVICE_NAME(destination_);
SDBUS_CHECK_OBJECT_PATH(objectPath_);
// The connection is ours only, i.e. it's us who has to manage the event loop upon this connection, // The connection is ours only, i.e. it's us who has to manage the event loop upon this connection,
// in order that we get and process signals, async call replies, and other messages from D-Bus. // in order that we get and process signals, async call replies, and other messages from D-Bus.
connection_->enterEventLoopAsync(); connection_->enterEventLoopAsync();
@ -67,14 +74,14 @@ MethodCall Proxy::createMethodCall(const std::string& interfaceName, const std::
MethodReply Proxy::callMethod(const MethodCall& message, uint64_t timeout) MethodReply Proxy::callMethod(const MethodCall& message, uint64_t timeout)
{ {
// Sending method call synchronously is the only operation that blocks, waiting for the method // Sending method call synchronously is the only operation that blocks, waiting for the method
// reply message among the incoming message on the sd-bus connection socket. But typically there // reply message among the incoming messages on the sd-bus connection socket. But typically there
// already is somebody that generally handles incoming D-Bus messages -- the connection event loop // already is somebody that generally handles incoming D-Bus messages -- the connection event loop
// running typically in its own thread. We have to avoid polling on socket from several threads. // running typically in its own thread. We have to avoid polling on socket from several threads.
// So we have to branch here: either we are within the context of the event loop thread, then we // So we have to branch here: either we are within the context of the event loop thread, then we
// can send the message simply via sd_bus_call, which blocks. Or we are in another thread, then // can send the message simply via sd_bus_call, which blocks. Or we are in another thread, then
// we can perform the send operation of the method call message from here (because that is thread- // we can perform the send operation of the method call message from here (because that is thread-
// safe like other sd-bus API accesses), but the incoming reply we have to get through the event // safe like other sd-bus API accesses), but the incoming reply we have to get through the event
// loop thread, because this is be the only rightful listener on the sd-bus connection socket. // loop thread, because this is the only rightful listener on the sd-bus connection socket.
// So, technically, we use async means to wait here for reply received by the event loop thread. // So, technically, we use async means to wait here for reply received by the event loop thread.
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid method call message provided", EINVAL); SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid method call message provided", EINVAL);
@ -120,7 +127,7 @@ MethodReply Proxy::sendMethodCallMessageAndWaitForReply(const MethodCall& messag
auto callback = (void*)&Proxy::sdbus_async_reply_handler; auto callback = (void*)&Proxy::sdbus_async_reply_handler;
AsyncCalls::CallData callData{*this, std::move(asyncReplyCallback), {}}; AsyncCalls::CallData callData{*this, std::move(asyncReplyCallback), {}};
message.send(callback, &callData, timeout, dont_request_slot); message.send(callback, &callData, timeout, floating_slot);
return syncCallReplyData.waitForMethodReply(); return syncCallReplyData.waitForMethodReply();
} }
@ -156,17 +163,28 @@ void Proxy::registerSignalHandler( const std::string& interfaceName
, const std::string& signalName , const std::string& signalName
, signal_handler signalHandler ) , signal_handler signalHandler )
{ {
SDBUS_CHECK_INTERFACE_NAME(interfaceName);
SDBUS_CHECK_MEMBER_NAME(signalName);
SDBUS_THROW_ERROR_IF(!signalHandler, "Invalid signal handler provided", EINVAL); SDBUS_THROW_ERROR_IF(!signalHandler, "Invalid signal handler provided", EINVAL);
auto& interface = interfaces_[interfaceName]; auto& interface = interfaces_[interfaceName];
InterfaceData::SignalData signalData{std::move(signalHandler), nullptr}; auto signalData = std::make_unique<InterfaceData::SignalData>(*this, std::move(signalHandler), nullptr);
auto insertionResult = interface.signals_.emplace(signalName, std::move(signalData)); auto insertionResult = interface.signals_.emplace(signalName, std::move(signalData));
auto inserted = insertionResult.second; auto inserted = insertionResult.second;
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register signal handler: handler already exists", EINVAL); SDBUS_THROW_ERROR_IF(!inserted, "Failed to register signal handler: handler already exists", EINVAL);
} }
void Proxy::unregisterSignalHandler( const std::string& interfaceName
, const std::string& signalName )
{
auto it = interfaces_.find(interfaceName);
if (it != interfaces_.end())
it->second.signals_.erase(signalName);
}
void Proxy::finishRegistration() void Proxy::finishRegistration()
{ {
registerSignalHandlers(*connection_); registerSignalHandlers(*connection_);
@ -182,12 +200,13 @@ void Proxy::registerSignalHandlers(sdbus::internal::IConnection& connection)
for (auto& signalItem : signalsOnInterface) for (auto& signalItem : signalsOnInterface)
{ {
const auto& signalName = signalItem.first; const auto& signalName = signalItem.first;
auto& slot = signalItem.second.slot_; auto* signalData = signalItem.second.get();
slot = connection.registerSignalHandler( objectPath_ signalData->slot = connection.registerSignalHandler( destination_
, interfaceName , objectPath_
, signalName , interfaceName
, &Proxy::sdbus_signal_handler , signalName
, this ); , &Proxy::sdbus_signal_handler
, signalData);
} }
} }
} }
@ -198,11 +217,21 @@ void Proxy::unregister()
interfaces_.clear(); interfaces_.clear();
} }
sdbus::IConnection& Proxy::getConnection() const
{
return *connection_;
}
const std::string& Proxy::getObjectPath() const const std::string& Proxy::getObjectPath() const
{ {
return objectPath_; return objectPath_;
} }
const Message* Proxy::getCurrentlyProcessedMessage() const
{
return m_CurrentlyProcessedMessage.load(std::memory_order_relaxed);
}
int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/) int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/)
{ {
auto* asyncCallData = static_cast<AsyncCalls::CallData*>(userData); auto* asyncCallData = static_cast<AsyncCalls::CallData*>(userData);
@ -223,32 +252,55 @@ int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userDat
auto message = Message::Factory::create<MethodReply>(sdbusMessage, &proxy.connection_->getSdBusInterface()); auto message = Message::Factory::create<MethodReply>(sdbusMessage, &proxy.connection_->getSdBusInterface());
const auto* error = sd_bus_message_get_error(sdbusMessage); proxy.m_CurrentlyProcessedMessage.store(&message, std::memory_order_relaxed);
if (error == nullptr) SCOPE_EXIT
{ {
asyncCallData->callback(message, nullptr); proxy.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
};
try
{
const auto* error = sd_bus_message_get_error(sdbusMessage);
if (error == nullptr)
{
asyncCallData->callback(message, nullptr);
}
else
{
Error exception(error->name, error->message);
asyncCallData->callback(message, &exception);
}
} }
else catch (const Error&)
{ {
sdbus::Error exception(error->name, error->message); // Intentionally left blank -- sdbus-c++ exceptions shall not bubble up to the underlying C sd-bus library
asyncCallData->callback(message, &exception);
} }
return 1; return 0;
} }
int Proxy::sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/) int Proxy::sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/)
{ {
auto* proxy = static_cast<Proxy*>(userData); auto* signalData = static_cast<InterfaceData::SignalData*>(userData);
assert(proxy != nullptr); assert(signalData != nullptr);
assert(signalData->callback);
auto message = Message::Factory::create<Signal>(sdbusMessage, &proxy->connection_->getSdBusInterface()); auto message = Message::Factory::create<Signal>(sdbusMessage, &signalData->proxy.connection_->getSdBusInterface());
// Note: The lookup can be optimized by using sorted vectors instead of associative containers signalData->proxy.m_CurrentlyProcessedMessage.store(&message, std::memory_order_relaxed);
auto& callback = proxy->interfaces_[message.getInterfaceName()].signals_[message.getMemberName()].callback_; SCOPE_EXIT
assert(callback); {
signalData->proxy.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
};
callback(message); try
{
signalData->callback(message);
}
catch (const Error&)
{
// Intentionally left blank -- sdbus-c++ exceptions shall not bubble up to the underlying C sd-bus library
}
return 0; return 0;
} }

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file Proxy.h * @file Proxy.h
* *
@ -35,6 +35,7 @@
#include <map> #include <map>
#include <unordered_map> #include <unordered_map>
#include <mutex> #include <mutex>
#include <atomic>
#include <condition_variable> #include <condition_variable>
namespace sdbus::internal { namespace sdbus::internal {
@ -57,10 +58,15 @@ namespace sdbus::internal {
void registerSignalHandler( const std::string& interfaceName void registerSignalHandler( const std::string& interfaceName
, const std::string& signalName , const std::string& signalName
, signal_handler signalHandler ) override; , signal_handler signalHandler ) override;
void unregisterSignalHandler( const std::string& interfaceName
, const std::string& signalName ) override;
void finishRegistration() override; void finishRegistration() override;
void unregister() override; void unregister() override;
sdbus::IConnection& getConnection() const override;
const std::string& getObjectPath() const override; const std::string& getObjectPath() const override;
const Message* getCurrentlyProcessedMessage() const override;
private: private:
class SyncCallReplyData class SyncCallReplyData
@ -97,10 +103,20 @@ namespace sdbus::internal {
using SignalName = std::string; using SignalName = std::string;
struct SignalData struct SignalData
{ {
signal_handler callback_; SignalData(Proxy& proxy, signal_handler callback, Slot slot)
SlotPtr slot_; : proxy(proxy)
, callback(std::move(callback))
, slot(std::move(slot))
{}
Proxy& proxy;
signal_handler callback;
// slot must be listed after callback to ensure that slot is destructed first.
// Destructing the slot will sd_bus_slot_unref() the callback.
// Only after sd_bus_slot_unref(), we can safely delete the callback. The bus mutex (SdBus::sdbusMutex_)
// ensures that sd_bus_slot_unref() and the callback execute sequentially.
Slot slot;
}; };
std::map<SignalName, SignalData> signals_; std::map<SignalName, std::unique_ptr<SignalData>> signals_;
}; };
std::map<InterfaceName, InterfaceData> interfaces_; std::map<InterfaceName, InterfaceData> interfaces_;
@ -115,7 +131,7 @@ namespace sdbus::internal {
{ {
Proxy& proxy; Proxy& proxy;
async_reply_handler callback; async_reply_handler callback;
MethodCall::Slot slot; Slot slot;
}; };
~AsyncCalls() ~AsyncCalls()
@ -149,6 +165,7 @@ namespace sdbus::internal {
{ {
std::unique_lock lock(mutex_); std::unique_lock lock(mutex_);
auto asyncCallSlots = std::move(calls_); auto asyncCallSlots = std::move(calls_);
calls_ = {};
lock.unlock(); lock.unlock();
// Releasing call slot pointer acquires global sd-bus mutex. We have to perform the release // Releasing call slot pointer acquires global sd-bus mutex. We have to perform the release
@ -158,9 +175,11 @@ namespace sdbus::internal {
} }
private: private:
std::unordered_map<void*, std::shared_ptr<CallData>> calls_;
std::mutex mutex_; std::mutex mutex_;
std::unordered_map<void*, std::shared_ptr<CallData>> calls_;
} pendingAsyncCalls_; } pendingAsyncCalls_;
std::atomic<const Message*> m_CurrentlyProcessedMessage{nullptr};
}; };
} }

View File

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

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file SdBus.cpp * @file SdBus.cpp
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru) * @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@ -161,9 +161,9 @@ int SdBus::sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, ch
return ::sd_bus_emit_interfaces_removed_strv(bus, path, interfaces); return ::sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
} }
int SdBus::sd_bus_open_user(sd_bus **ret) int SdBus::sd_bus_open(sd_bus **ret)
{ {
return ::sd_bus_open_user(ret); return ::sd_bus_open(ret);
} }
int SdBus::sd_bus_open_system(sd_bus **ret) int SdBus::sd_bus_open_system(sd_bus **ret)
@ -171,6 +171,43 @@ int SdBus::sd_bus_open_system(sd_bus **ret)
return ::sd_bus_open_system(ret); return ::sd_bus_open_system(ret);
} }
int SdBus::sd_bus_open_user(sd_bus **ret)
{
return ::sd_bus_open_user(ret);
}
int SdBus::sd_bus_open_user_with_address(sd_bus **ret, const char* address)
{
sd_bus* bus = nullptr;
int r = sd_bus_new(&bus);
if (r < 0)
return r;
r = sd_bus_set_address(bus, address);
if (r < 0)
return r;
r = sd_bus_set_bus_client(bus, true);
if (r < 0)
return r;
// Copying behavior from
// https://github.com/systemd/systemd/blob/fee6441601c979165ebcbb35472036439f8dad5f/src/libsystemd/sd-bus/sd-bus.c#L1381
// Here, we make the bus as trusted
r = sd_bus_set_trusted(bus, true);
if (r < 0)
return r;
r = sd_bus_start(bus);
if (r < 0)
return r;
*ret = bus;
return 0;
}
int SdBus::sd_bus_open_system_remote(sd_bus **ret, const char *host) int SdBus::sd_bus_open_system_remote(sd_bus **ret, const char *host)
{ {
return ::sd_bus_open_system_remote(ret, host); return ::sd_bus_open_system_remote(ret, host);
@ -224,6 +261,16 @@ sd_bus_slot* SdBus::sd_bus_slot_unref(sd_bus_slot *slot)
return ::sd_bus_slot_unref(slot); return ::sd_bus_slot_unref(slot);
} }
int SdBus::sd_bus_new(sd_bus **ret)
{
return ::sd_bus_new(ret);
}
int SdBus::sd_bus_start(sd_bus *bus)
{
return ::sd_bus_start(bus);
}
int SdBus::sd_bus_process(sd_bus *bus, sd_bus_message **r) int SdBus::sd_bus_process(sd_bus *bus, sd_bus_message **r)
{ {
std::lock_guard lock(sdbusMutex_); std::lock_guard lock(sdbusMutex_);
@ -260,4 +307,84 @@ sd_bus* SdBus::sd_bus_flush_close_unref(sd_bus *bus)
return ::sd_bus_flush_close_unref(bus); return ::sd_bus_flush_close_unref(bus);
} }
sd_bus* SdBus::sd_bus_close_unref(sd_bus *bus)
{
#if LIBSYSTEMD_VERSION>=241
return ::sd_bus_close_unref(bus);
#else
::sd_bus_close(bus);
return ::sd_bus_unref(bus);
#endif
}
int SdBus::sd_bus_message_set_destination(sd_bus_message *m, const char *destination)
{
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_message_set_destination(m, destination);
}
int SdBus::sd_bus_query_sender_creds(sd_bus_message *m, uint64_t mask, sd_bus_creds **c)
{
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_query_sender_creds(m, mask, c);
}
sd_bus_creds* SdBus::sd_bus_creds_unref(sd_bus_creds *c)
{
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_creds_unref(c);
}
int SdBus::sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid)
{
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_creds_get_pid(c, pid);
}
int SdBus::sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid)
{
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_creds_get_uid(c, uid);
}
int SdBus::sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *euid)
{
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_creds_get_euid(c, euid);
}
int SdBus::sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid)
{
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_creds_get_gid(c, gid);
}
int SdBus::sd_bus_creds_get_egid(sd_bus_creds *c, uid_t *egid)
{
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_creds_get_egid(c, egid);
}
int SdBus::sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids)
{
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_creds_get_supplementary_gids(c, gids);
}
int SdBus::sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **label)
{
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_creds_get_selinux_context(c, label);
}
} }

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file SdBus.h * @file SdBus.h
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru) * @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@ -58,8 +58,10 @@ public:
virtual int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) override; virtual int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) override;
virtual int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) override; virtual int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) override;
virtual int sd_bus_open_user(sd_bus **ret) override; virtual int sd_bus_open(sd_bus **ret) override;
virtual int sd_bus_open_system(sd_bus **ret) override; virtual int sd_bus_open_system(sd_bus **ret) override;
virtual int sd_bus_open_user(sd_bus **ret) override;
virtual int sd_bus_open_user_with_address(sd_bus **ret, const char* address) override;
virtual int sd_bus_open_system_remote(sd_bus **ret, const char* hsot) override; virtual int sd_bus_open_system_remote(sd_bus **ret, const char* hsot) override;
virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) override; virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) override;
virtual int sd_bus_release_name(sd_bus *bus, const char *name) override; virtual int sd_bus_release_name(sd_bus *bus, const char *name) override;
@ -69,11 +71,28 @@ public:
virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) override; virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) override;
virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) override; virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) override;
virtual int sd_bus_new(sd_bus **ret) override;
virtual int sd_bus_start(sd_bus *bus) override;
virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) override; virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) override;
virtual int sd_bus_get_poll_data(sd_bus *bus, PollData* data) override; virtual int sd_bus_get_poll_data(sd_bus *bus, PollData* data) override;
virtual int sd_bus_flush(sd_bus *bus) override; virtual int sd_bus_flush(sd_bus *bus) override;
virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) override; virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) override;
virtual sd_bus *sd_bus_close_unref(sd_bus *bus) override;
virtual int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) override;
virtual int sd_bus_query_sender_creds(sd_bus_message *m, uint64_t mask, sd_bus_creds **c) override;
virtual sd_bus_creds* sd_bus_creds_unref(sd_bus_creds *c) override;
virtual int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) override;
virtual int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid) override;
virtual int sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *euid) override;
virtual int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid) override;
virtual int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid) override;
virtual int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids) override;
virtual int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **label) override;
private: private:
std::recursive_mutex sdbusMutex_; std::recursive_mutex sdbusMutex_;

View File

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

53
src/Utils.h Normal file
View File

@ -0,0 +1,53 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Utils.h
*
* Created on: Feb 9, 2022
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SDBUS_CXX_INTERNAL_UTILS_H_
#define SDBUS_CXX_INTERNAL_UTILS_H_
#include <sdbus-c++/Error.h>
#include <systemd/sd-bus.h>
#if LIBSYSTEMD_VERSION>=246
#define SDBUS_CHECK_OBJECT_PATH(_PATH) \
SDBUS_THROW_ERROR_IF(!sd_bus_object_path_is_valid(_PATH.c_str()), "Invalid object path '" + _PATH + "' provided", EINVAL) \
/**/
#define SDBUS_CHECK_INTERFACE_NAME(_NAME) \
SDBUS_THROW_ERROR_IF(!sd_bus_interface_name_is_valid(_NAME.c_str()), "Invalid interface name '" + _NAME + "' provided", EINVAL) \
/**/
#define SDBUS_CHECK_SERVICE_NAME(_NAME) \
SDBUS_THROW_ERROR_IF(!sd_bus_service_name_is_valid(_NAME.c_str()), "Invalid service name '" + _NAME + "' provided", EINVAL) \
/**/
#define SDBUS_CHECK_MEMBER_NAME(_NAME) \
SDBUS_THROW_ERROR_IF(!sd_bus_member_name_is_valid(_NAME.c_str()), "Invalid member name '" + _NAME + "' provided", EINVAL) \
/**/
#else
#define SDBUS_CHECK_OBJECT_PATH(_PATH)
#define SDBUS_CHECK_INTERFACE_NAME(_NAME)
#define SDBUS_CHECK_SERVICE_NAME(_NAME)
#define SDBUS_CHECK_MEMBER_NAME(_NAME)
#endif
#endif /* SDBUS_CXX_INTERNAL_UTILS_H_ */

View File

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

View File

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

View File

@ -2,26 +2,44 @@
# DOWNLOAD AND BUILD OF GOOGLETEST # DOWNLOAD AND BUILD OF GOOGLETEST
#------------------------------- #-------------------------------
include(FetchContent) set(GOOGLETEST_VERSION 1.10.0 CACHE STRING "Version of gmock to use")
set(GOOGLETEST_GIT_REPO "https://github.com/google/googletest.git" CACHE STRING "A git repo to clone and build googletest from if gmock is not found in the system")
message("Fetching googletest...") find_package(GTest ${GOOGLETEST_VERSION} CONFIG)
FetchContent_Declare(googletest if (NOT TARGET GTest::gmock)
GIT_REPOSITORY https://github.com/google/googletest.git # Try pkg-config if GTest was not found through CMake config
GIT_TAG master find_package(PkgConfig)
GIT_SHALLOW 1 if (PkgConfig_FOUND)
UPDATE_COMMAND "") pkg_check_modules(GMock IMPORTED_TARGET GLOBAL gmock>=${GOOGLETEST_VERSION})
if(TARGET PkgConfig::GMock)
add_library(GTest::gmock ALIAS PkgConfig::GMock)
endif()
endif()
# GTest was not found in the system, build it on our own
if (NOT TARGET GTest::gmock)
include(FetchContent)
#FetchContent_MakeAvailable(googletest) # Not available in CMake 3.13 :-( Let's do it manually: message("Fetching googletest v${GOOGLETEST_VERSION}...")
FetchContent_GetProperties(googletest) FetchContent_Declare(googletest
if(NOT googletest_POPULATED) GIT_REPOSITORY ${GOOGLETEST_GIT_REPO}
FetchContent_Populate(googletest) GIT_TAG release-${GOOGLETEST_VERSION}
set(gtest_force_shared_crt ON CACHE INTERNAL "" FORCE) GIT_SHALLOW 1
set(BUILD_GMOCK ON CACHE INTERNAL "" FORCE) UPDATE_COMMAND "")
set(INSTALL_GTEST OFF CACHE INTERNAL "" FORCE)
set(BUILD_SHARED_LIBS_BAK ${BUILD_SHARED_LIBS}) #FetchContent_MakeAvailable(googletest) # Not available in CMake 3.13 :-( Let's do it manually:
set(BUILD_SHARED_LIBS OFF) FetchContent_GetProperties(googletest)
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) if(NOT googletest_POPULATED)
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_BAK}) FetchContent_Populate(googletest)
set(gtest_force_shared_crt ON CACHE INTERNAL "" FORCE)
set(BUILD_GMOCK ON CACHE INTERNAL "" FORCE)
set(INSTALL_GTEST OFF CACHE INTERNAL "" FORCE)
set(BUILD_SHARED_LIBS_BAK ${BUILD_SHARED_LIBS})
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_BAK})
add_library(GTest::gmock ALIAS gmock)
endif()
endif()
endif() endif()
#------------------------------- #-------------------------------
@ -39,14 +57,23 @@ set(UNITTESTS_SRCS
set(INTEGRATIONTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/integrationtests) set(INTEGRATIONTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/integrationtests)
set(INTEGRATIONTESTS_SRCS set(INTEGRATIONTESTS_SRCS
${INTEGRATIONTESTS_SOURCE_DIR}/AdaptorAndProxy_test.cpp ${INTEGRATIONTESTS_SOURCE_DIR}/DBusConnectionTests.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/Connection_test.cpp ${INTEGRATIONTESTS_SOURCE_DIR}/DBusGeneralTests.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/sdbus-c++-integration-tests.cpp ${INTEGRATIONTESTS_SOURCE_DIR}/DBusMethodsTests.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/adaptor-glue.h ${INTEGRATIONTESTS_SOURCE_DIR}/DBusAsyncMethodsTests.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/defs.h ${INTEGRATIONTESTS_SOURCE_DIR}/DBusSignalsTests.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/proxy-glue.h ${INTEGRATIONTESTS_SOURCE_DIR}/DBusPropertiesTests.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/TestingAdaptor.h ${INTEGRATIONTESTS_SOURCE_DIR}/DBusStandardInterfacesTests.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/TestingProxy.h) ${INTEGRATIONTESTS_SOURCE_DIR}/Defs.h
${INTEGRATIONTESTS_SOURCE_DIR}/integrationtests-adaptor.h
${INTEGRATIONTESTS_SOURCE_DIR}/integrationtests-proxy.h
${INTEGRATIONTESTS_SOURCE_DIR}/TestFixture.h
${INTEGRATIONTESTS_SOURCE_DIR}/TestFixture.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/TestAdaptor.h
${INTEGRATIONTESTS_SOURCE_DIR}/TestAdaptor.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/TestProxy.h
${INTEGRATIONTESTS_SOURCE_DIR}/TestProxy.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/sdbus-c++-integration-tests.cpp)
set(PERFTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/perftests) set(PERFTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/perftests)
set(STRESSTESTS_CLIENT_SRCS set(STRESSTESTS_CLIENT_SRCS
@ -78,11 +105,11 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_executable(sdbus-c++-unit-tests ${UNITTESTS_SRCS}) add_executable(sdbus-c++-unit-tests ${UNITTESTS_SRCS})
target_compile_definitions(sdbus-c++-unit-tests PRIVATE LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION}) target_compile_definitions(sdbus-c++-unit-tests PRIVATE LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION})
target_link_libraries(sdbus-c++-unit-tests sdbus-c++-objlib gmock gmock_main) target_link_libraries(sdbus-c++-unit-tests sdbus-c++-objlib GTest::gmock)
add_executable(sdbus-c++-integration-tests ${INTEGRATIONTESTS_SRCS}) add_executable(sdbus-c++-integration-tests ${INTEGRATIONTESTS_SRCS})
target_compile_definitions(sdbus-c++-integration-tests PRIVATE LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION}) target_compile_definitions(sdbus-c++-integration-tests PRIVATE LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION})
target_link_libraries(sdbus-c++-integration-tests sdbus-c++ gmock gmock_main) target_link_libraries(sdbus-c++-integration-tests sdbus-c++ GTest::gmock)
# Manual performance and stress tests # Manual performance and stress tests
option(ENABLE_PERF_TESTS "Build and install manual performance tests (default OFF)" OFF) option(ENABLE_PERF_TESTS "Build and install manual performance tests (default OFF)" OFF)
@ -93,6 +120,7 @@ if(ENABLE_PERF_TESTS OR ENABLE_STRESS_TESTS)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
if(ENABLE_PERF_TESTS) if(ENABLE_PERF_TESTS)
message(STATUS "Building with performance tests")
add_executable(sdbus-c++-perf-tests-client ${STRESSTESTS_CLIENT_SRCS}) add_executable(sdbus-c++-perf-tests-client ${STRESSTESTS_CLIENT_SRCS})
target_link_libraries(sdbus-c++-perf-tests-client sdbus-c++ Threads::Threads) target_link_libraries(sdbus-c++-perf-tests-client sdbus-c++ Threads::Threads)
add_executable(sdbus-c++-perf-tests-server ${STRESSTESTS_SERVER_SRCS}) add_executable(sdbus-c++-perf-tests-server ${STRESSTESTS_SERVER_SRCS})
@ -100,6 +128,7 @@ if(ENABLE_PERF_TESTS OR ENABLE_STRESS_TESTS)
endif() endif()
if(ENABLE_STRESS_TESTS) if(ENABLE_STRESS_TESTS)
message(STATUS "Building with stress tests")
add_executable(sdbus-c++-stress-tests ${STRESSTESTS_SRCS}) add_executable(sdbus-c++-stress-tests ${STRESSTESTS_SRCS})
target_link_libraries(sdbus-c++-stress-tests sdbus-c++ Threads::Threads) target_link_libraries(sdbus-c++-stress-tests sdbus-c++ Threads::Threads)
endif() endif()
@ -111,19 +140,19 @@ endif()
set(TESTS_INSTALL_PATH "/opt/test/bin" CACHE STRING "Specifies where the test binaries will be installed") set(TESTS_INSTALL_PATH "/opt/test/bin" CACHE STRING "Specifies where the test binaries will be installed")
install(TARGETS sdbus-c++-unit-tests DESTINATION ${TESTS_INSTALL_PATH}) install(TARGETS sdbus-c++-unit-tests DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
install(TARGETS sdbus-c++-integration-tests DESTINATION ${TESTS_INSTALL_PATH}) install(TARGETS sdbus-c++-integration-tests DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
install(FILES ${INTEGRATIONTESTS_SOURCE_DIR}/files/org.sdbuscpp.integrationtests.conf DESTINATION /etc/dbus-1/system.d) install(FILES ${INTEGRATIONTESTS_SOURCE_DIR}/files/org.sdbuscpp.integrationtests.conf DESTINATION /etc/dbus-1/system.d COMPONENT test)
if(ENABLE_PERF_TESTS) if(ENABLE_PERF_TESTS)
install(TARGETS sdbus-c++-perf-tests-client DESTINATION ${TESTS_INSTALL_PATH}) install(TARGETS sdbus-c++-perf-tests-client DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
install(TARGETS sdbus-c++-perf-tests-server DESTINATION ${TESTS_INSTALL_PATH}) install(TARGETS sdbus-c++-perf-tests-server DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
install(FILES ${PERFTESTS_SOURCE_DIR}/files/org.sdbuscpp.perftests.conf DESTINATION /etc/dbus-1/system.d) install(FILES ${PERFTESTS_SOURCE_DIR}/files/org.sdbuscpp.perftests.conf DESTINATION /etc/dbus-1/system.d COMPONENT test)
endif() endif()
if(ENABLE_STRESS_TESTS) if(ENABLE_STRESS_TESTS)
install(TARGETS sdbus-c++-stress-tests DESTINATION ${TESTS_INSTALL_PATH}) install(TARGETS sdbus-c++-stress-tests DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
install(FILES ${STRESSTESTS_SOURCE_DIR}/files/org.sdbuscpp.stresstests.conf DESTINATION /etc/dbus-1/system.d) install(FILES ${STRESSTESTS_SOURCE_DIR}/files/org.sdbuscpp.stresstests.conf DESTINATION /etc/dbus-1/system.d COMPONENT test)
endif() endif()
#---------------------------------- #----------------------------------

View File

@ -1,19 +0,0 @@
# Taken from https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project
cmake_minimum_required(VERSION 3.6)
project(googletest-download NONE)
include(ExternalProject)
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
GIT_SHALLOW 1
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build"
UPDATE_COMMAND ""
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND "")

View File

@ -1,771 +0,0 @@
/**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file AdaptorAndProxy_test.cpp
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
// Own
#include "TestingAdaptor.h"
#include "TestingProxy.h"
// sdbus
#include "sdbus-c++/sdbus-c++.h"
// gmock
#include <gtest/gtest.h>
#include <gmock/gmock.h>
// STL
#include <string>
#include <thread>
#include <tuple>
#include <chrono>
#include <fstream>
#include <future>
#include <unistd.h>
using ::testing::Eq;
using ::testing::DoubleEq;
using ::testing::Gt;
using ::testing::AnyOf;
using ::testing::ElementsAre;
using ::testing::SizeIs;
using namespace std::chrono_literals;
namespace
{
class AdaptorAndProxyFixture : public ::testing::Test
{
public:
static void SetUpTestCase()
{
s_connection->requestName(INTERFACE_NAME);
s_connection->enterEventLoopAsync();
}
static void TearDownTestCase()
{
s_connection->leaveEventLoop();
s_connection->releaseName(INTERFACE_NAME);
}
template <typename _Fnc>
static bool waitUntil(_Fnc&& fnc, std::chrono::milliseconds timeout = 5s)
{
std::chrono::milliseconds elapsed{};
std::chrono::milliseconds step{5ms};
do {
std::this_thread::sleep_for(step);
elapsed += step;
if (elapsed > timeout)
return false;
} while (!fnc());
return true;
}
static bool waitUntil(std::atomic<bool>& flag, std::chrono::milliseconds timeout = 5s)
{
return waitUntil([&flag]() -> bool { return flag; }, timeout);
}
private:
void SetUp() override
{
m_adaptor = std::make_unique<TestingAdaptor>(*s_connection);
m_proxy = std::make_unique<TestingProxy>(INTERFACE_NAME, OBJECT_PATH);
std::this_thread::sleep_for(50ms); // Give time for the proxy to start listening to signals
}
void TearDown() override
{
m_proxy.reset();
m_adaptor.reset();
}
public:
static std::unique_ptr<sdbus::IConnection> s_connection;
std::unique_ptr<TestingAdaptor> m_adaptor;
std::unique_ptr<TestingProxy> m_proxy;
};
std::unique_ptr<sdbus::IConnection> AdaptorAndProxyFixture::s_connection = sdbus::createSystemBusConnection();
}
using SdbusTestObject = AdaptorAndProxyFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST(AdaptorAndProxy, CanBeConstructedSuccesfully)
{
auto connection = sdbus::createConnection();
connection->requestName(INTERFACE_NAME);
ASSERT_NO_THROW(TestingAdaptor adaptor(*connection));
ASSERT_NO_THROW(TestingProxy proxy(INTERFACE_NAME, OBJECT_PATH));
connection->releaseName(INTERFACE_NAME);
}
// Methods
TEST_F(SdbusTestObject, CallsEmptyMethodSuccesfully)
{
ASSERT_NO_THROW(m_proxy->noArgNoReturn());
}
TEST_F(SdbusTestObject, CallsMethodsWithBaseTypesSuccesfully)
{
auto resInt = m_proxy->getInt();
ASSERT_THAT(resInt, Eq(INT32_VALUE));
auto multiplyRes = m_proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
ASSERT_THAT(multiplyRes, Eq(INT64_VALUE * DOUBLE_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodsWithTuplesSuccesfully)
{
auto resTuple = m_proxy->getTuple();
ASSERT_THAT(std::get<0>(resTuple), Eq(UINT32_VALUE));
ASSERT_THAT(std::get<1>(resTuple), Eq(STRING_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodsWithStructSuccesfully)
{
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> a{};
auto vectorRes = m_proxy->getInts16FromStruct(a);
ASSERT_THAT(vectorRes, Eq(std::vector<int16_t>{0})); // because second item is by default initialized to 0
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> b{
UINT8_VALUE, INT16_VALUE, DOUBLE_VALUE, STRING_VALUE, {INT16_VALUE, -INT16_VALUE}
};
vectorRes = m_proxy->getInts16FromStruct(b);
ASSERT_THAT(vectorRes, Eq(std::vector<int16_t>{INT16_VALUE, INT16_VALUE, -INT16_VALUE}));
}
TEST_F(SdbusTestObject, CallsMethodWithVariantSuccesfully)
{
sdbus::Variant v{DOUBLE_VALUE};
auto variantRes = m_proxy->processVariant(v);
ASSERT_THAT(variantRes.get<int32_t>(), Eq(static_cast<int32_t>(DOUBLE_VALUE)));
}
TEST_F(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccesfully)
{
std::vector<int32_t> x{-2, 0, 2};
sdbus::Struct<sdbus::Variant, sdbus::Variant> y{false, true};
auto mapOfVariants = m_proxy->getMapOfVariants(x, y);
decltype(mapOfVariants) res{{-2, false}, {0, false}, {2, true}};
ASSERT_THAT(mapOfVariants[-2].get<bool>(), Eq(res[-2].get<bool>()));
ASSERT_THAT(mapOfVariants[0].get<bool>(), Eq(res[0].get<bool>()));
ASSERT_THAT(mapOfVariants[2].get<bool>(), Eq(res[2].get<bool>()));
}
TEST_F(SdbusTestObject, CallsMethodWithStructInStructSuccesfully)
{
auto val = m_proxy->getStructInStruct();
ASSERT_THAT(val.get<0>(), Eq(STRING_VALUE));
ASSERT_THAT(std::get<0>(std::get<1>(val))[INT32_VALUE], Eq(INT32_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithTwoStructsSuccesfully)
{
auto val = m_proxy->sumStructItems({1, 2}, {3, 4});
ASSERT_THAT(val, Eq(1 + 2 + 3 + 4));
}
TEST_F(SdbusTestObject, CallsMethodWithTwoVectorsSuccesfully)
{
auto val = m_proxy->sumVectorItems({1, 7}, {2, 3});
ASSERT_THAT(val, Eq(1 + 7 + 2 + 3));
}
TEST_F(SdbusTestObject, CallsMethodWithSignatureSuccesfully)
{
auto resSignature = m_proxy->getSignature();
ASSERT_THAT(resSignature, Eq(SIGNATURE_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithObjectPathSuccesfully)
{
auto resObjectPath = m_proxy->getObjPath();
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();
ASSERT_THAT(resComplex.count(0), Eq(1));
}
TEST_F(SdbusTestObject, CallsMultiplyMethodWithNoReplyFlag)
{
m_proxy->multiplyWithNoReply(INT64_VALUE, DOUBLE_VALUE);
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
{
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(), AnyOf("org.freedesktop.DBus.Error.Timeout", "org.freedesktop.DBus.Error.NoReply"));
ASSERT_THAT(e.getMessage(), AnyOf("Connection timed out", "Method call timed out"));
}
catch(...)
{
FAIL() << "Expected sdbus::Error exception";
}
}
TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTimesOut)
{
try
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
{
if (err == nullptr)
promise.set_value(res);
else
promise.set_exception(std::make_exception_ptr(*err));
});
m_proxy->doOperationClientSideAsyncWith500msTimeout(1000); // The operation will take 1s, but the timeout is 500ms, so we should time out
future.get(), Eq(100);
FAIL() << "Expected sdbus::Error exception";
}
catch (const sdbus::Error& e)
{
ASSERT_THAT(e.getName(), AnyOf("org.freedesktop.DBus.Error.Timeout", "org.freedesktop.DBus.Error.NoReply"));
ASSERT_THAT(e.getMessage(), AnyOf("Connection timed out", "Method call timed out"));
}
catch(...)
{
FAIL() << "Expected sdbus::Error exception";
}
}
TEST_F(SdbusTestObject, CallsMethodThatThrowsError)
{
try
{
m_proxy->throwError();
FAIL() << "Expected sdbus::Error exception";
}
catch (const sdbus::Error& e)
{
ASSERT_THAT(e.getName(), Eq("org.freedesktop.DBus.Error.AccessDenied"));
ASSERT_THAT(e.getMessage(), Eq("A test error occurred (Operation not permitted)"));
}
catch(...)
{
FAIL() << "Expected sdbus::Error exception";
}
}
TEST_F(SdbusTestObject, CallsErrorThrowingMethodWithDontExpectReplySet)
{
ASSERT_NO_THROW(m_proxy->throwErrorWithNoReply());
ASSERT_TRUE(waitUntil(m_adaptor->m_wasThrowErrorCalled));
}
TEST_F(SdbusTestObject, RunsServerSideAsynchoronousMethodAsynchronously)
{
// Yeah, this is kinda timing-dependent test, but times should be safe...
std::mutex mtx;
std::vector<uint32_t> results;
std::atomic<bool> invoke{};
std::atomic<int> startedCount{};
auto call = [&](uint32_t param)
{
TestingProxy proxy{INTERFACE_NAME, OBJECT_PATH};
++startedCount;
while (!invoke) ;
auto result = proxy.doOperationAsync(param);
std::lock_guard<std::mutex> guard(mtx);
results.push_back(result);
};
std::thread invocations[]{std::thread{call, 1500}, std::thread{call, 1000}, std::thread{call, 500}};
while (startedCount != 3) ;
invoke = true;
std::for_each(std::begin(invocations), std::end(invocations), [](auto& t){ t.join(); });
ASSERT_THAT(results, ElementsAre(500, 1000, 1500));
}
TEST_F(SdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
{
std::atomic<size_t> resultCount{};
std::atomic<bool> invoke{};
std::atomic<int> startedCount{};
auto call = [&]()
{
TestingProxy proxy{INTERFACE_NAME, OBJECT_PATH};
++startedCount;
while (!invoke) ;
size_t localResultCount{};
for (size_t i = 0; i < 500; ++i)
{
auto result = proxy.doOperationAsync(i % 2);
if (result == (i % 2)) // Correct return value?
localResultCount++;
}
resultCount += localResultCount;
};
std::thread invocations[]{std::thread{call}, std::thread{call}, std::thread{call}};
while (startedCount != 3) ;
invoke = true;
std::for_each(std::begin(invocations), std::end(invocations), [](auto& t){ t.join(); });
ASSERT_THAT(resultCount, Eq(1500));
}
TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSide)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
{
if (err == nullptr)
promise.set_value(res);
else
promise.set_exception(std::make_exception_ptr(*err));
});
m_proxy->doOperationClientSideAsync(100);
ASSERT_THAT(future.get(), Eq(100));
}
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsPendingIfItIsInProgress)
{
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){});
auto call = m_proxy->doOperationClientSideAsync(100);
ASSERT_TRUE(call.isPending());
}
TEST_F(SdbusTestObject, CancelsPendingAsyncCallOnClientSide)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
auto call = m_proxy->doOperationClientSideAsync(100);
call.cancel();
ASSERT_THAT(future.wait_for(300ms), Eq(std::future_status::timeout));
}
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCancelled)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
auto call = m_proxy->doOperationClientSideAsync(100);
call.cancel();
ASSERT_FALSE(call.isPending());
}
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCompleted)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
auto call = m_proxy->doOperationClientSideAsync(0);
(void) future.get(); // Wait for the call to finish
ASSERT_TRUE(waitUntil([&call](){ return !call.isPending(); }));
}
TEST_F(SdbusTestObject, InvokesErroneousMethodAsynchronouslyOnClientSide)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
{
if (err == nullptr)
promise.set_value(res);
else
promise.set_exception(std::make_exception_ptr(*err));
});
m_proxy->doErroneousOperationClientSideAsync();
ASSERT_THROW(future.get(), sdbus::Error);
}
TEST_F(SdbusTestObject, FailsCallingNonexistentMethod)
{
ASSERT_THROW(m_proxy->callNonexistentMethod(), sdbus::Error);
}
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentInterface)
{
ASSERT_THROW(m_proxy->callMethodOnNonexistentInterface(), sdbus::Error);
}
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentDestination)
{
TestingProxy proxy("sdbuscpp.destination.that.does.not.exist", OBJECT_PATH);
ASSERT_THROW(proxy.getInt(), sdbus::Error);
}
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentObject)
{
TestingProxy proxy(INTERFACE_NAME, "/sdbuscpp/path/that/does/not/exist");
ASSERT_THROW(proxy.getInt(), sdbus::Error);
}
TEST_F(SdbusTestObject, ReceivesTwoSignalsWhileMakingMethodCall)
{
m_proxy->emitTwoSimpleSignals();
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithMap));
}
#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)
{
m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
}
TEST_F(SdbusTestObject, EmitsSimpleSignalToMultipleProxiesSuccesfully)
{
auto proxy1 = std::make_unique<TestingProxy>(*s_connection, INTERFACE_NAME, OBJECT_PATH);
auto proxy2 = std::make_unique<TestingProxy>(*s_connection, INTERFACE_NAME, OBJECT_PATH);
m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(proxy1->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(proxy2->m_gotSimpleSignal));
}
TEST_F(SdbusTestObject, EmitsSignalWithMapSuccesfully)
{
m_adaptor->emitSignalWithMap({{0, "zero"}, {1, "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->emitSignalWithVariant(d);
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithVariant));
ASSERT_THAT(m_proxy->m_variantFromSignal, DoubleEq(d));
}
TEST_F(SdbusTestObject, EmitsSignalWithoutRegistrationSuccesfully)
{
m_adaptor->emitSignalWithoutRegistration({"platform", {"av"}});
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithSignature));
ASSERT_THAT(m_proxy->m_signatureFromSignal["platform"], Eq("av"));
}
// Properties
TEST_F(SdbusTestObject, ReadsReadOnlyPropertySuccesfully)
{
ASSERT_THAT(m_proxy->state(), Eq(DEFAULT_STATE_VALUE));
}
TEST_F(SdbusTestObject, FailsWritingToReadOnlyProperty)
{
ASSERT_THROW(m_proxy->state("new_value"), sdbus::Error);
}
TEST_F(SdbusTestObject, WritesAndReadsReadWritePropertySuccesfully)
{
uint32_t newActionValue = 5678;
m_proxy->action(newActionValue);
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
}
// Standard D-Bus interfaces
TEST_F(SdbusTestObject, PingsViaPeerInterface)
{
ASSERT_NO_THROW(m_proxy->Ping());
}
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());
}
TEST_F(SdbusTestObject, AnswersXmlApiDescriptionViaIntrospectableInterface)
{
ASSERT_THAT(m_proxy->Introspect(), Eq(m_adaptor->getExpectedXmlApiDescription()));
}
TEST_F(SdbusTestObject, GetsPropertyViaPropertiesInterface)
{
ASSERT_THAT(m_proxy->Get(INTERFACE_NAME, "state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
}
TEST_F(SdbusTestObject, SetsPropertyViaPropertiesInterface)
{
uint32_t newActionValue = 2345;
m_proxy->Set(INTERFACE_NAME, "action", newActionValue);
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
}
TEST_F(SdbusTestObject, GetsAllPropertiesViaPropertiesInterface)
{
const auto properties = m_proxy->GetAll(INTERFACE_NAME);
ASSERT_THAT(properties, SizeIs(3));
EXPECT_THAT(properties.at("state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
EXPECT_THAT(properties.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(properties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
}
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*/ )
{
EXPECT_THAT(interfaceName, Eq(INTERFACE_NAME));
EXPECT_THAT(changedProperties, SizeIs(1));
EXPECT_THAT(changedProperties.at("blocking").get<bool>(), Eq(!DEFAULT_BLOCKING_VALUE));
signalReceived = true;
};
m_proxy->blocking(!DEFAULT_BLOCKING_VALUE);
m_proxy->action(DEFAULT_ACTION_VALUE*2);
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME, {"blocking"});
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForAllProperties)
{
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 )
{
EXPECT_THAT(interfaceName, Eq(INTERFACE_NAME));
EXPECT_THAT(changedProperties, SizeIs(1));
EXPECT_THAT(changedProperties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
ASSERT_THAT(invalidatedProperties, SizeIs(1));
EXPECT_THAT(invalidatedProperties[0], Eq("action"));
signalReceived = true;
};
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME);
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, GetsZeroManagedObjectsIfHasNoSubPathObjects)
{
const auto objectsInterfacesAndProperties = m_proxy->GetManagedObjects();
ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(0));
}
TEST_F(SdbusTestObject, GetsManagedObjectsSuccessfully)
{
auto subObject1 = sdbus::createObject(*s_connection, "/sub/path1");
subObject1->registerProperty("aProperty1").onInterface("org.sdbuscpp.integrationtests.iface1").withGetter([]{return uint8_t{123};});
subObject1->finishRegistration();
auto subObject2 = sdbus::createObject(*s_connection, "/sub/path2");
subObject2->registerProperty("aProperty2").onInterface("org.sdbuscpp.integrationtests.iface2").withGetter([]{return "hi";});
subObject2->finishRegistration();
const auto objectsInterfacesAndProperties = m_proxy->GetManagedObjects();
ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(2));
EXPECT_THAT(objectsInterfacesAndProperties.at("/sub/path1").at("org.sdbuscpp.integrationtests.iface1").at("aProperty1").get<uint8_t>(), Eq(123));
EXPECT_THAT(objectsInterfacesAndProperties.at("/sub/path2").at("org.sdbuscpp.integrationtests.iface2").at("aProperty2").get<std::string>(), Eq("hi"));
}
TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
m_proxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
EXPECT_THAT(interfacesAndProperties, SizeIs(1));
EXPECT_THAT(interfacesAndProperties.count(INTERFACE_NAME), Eq(1));
#if LIBSYSTEMD_VERSION<=244
// Up to sd-bus v244, all properties are added to the list, i.e. `state', `action', and `blocking' in this case.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("action"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
#else
// Since v245 sd-bus does not add to the InterfacesAdded signal message the values of properties marked only
// for invalidation on change, which makes the behavior consistent with the PropertiesChangedSignal.
// So in this specific instance, `action' property is no more added to the list.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(2));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
#endif
signalReceived = true;
};
m_adaptor->emitInterfacesAddedSignal({INTERFACE_NAME});
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
m_proxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
EXPECT_THAT(interfacesAndProperties, SizeIs(5)); // INTERFACE_NAME + 4 standard interfaces
#if LIBSYSTEMD_VERSION<=244
// Up to sd-bus v244, all properties are added to the list, i.e. `state', `action', and `blocking' in this case.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("action"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
#else
// Since v245 sd-bus does not add to the InterfacesAdded signal message the values of properties marked only
// for invalidation on change, which makes the behavior consistent with the PropertiesChangedSignal.
// So in this specific instance, `action' property is no more added to the list.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(2));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
#endif
signalReceived = true;
};
m_adaptor->emitInterfacesAddedSignal();
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
m_proxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
ASSERT_THAT(interfaces, SizeIs(1));
EXPECT_THAT(interfaces[0], Eq(INTERFACE_NAME));
signalReceived = true;
};
m_adaptor->emitInterfacesRemovedSignal({INTERFACE_NAME});
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
m_proxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
ASSERT_THAT(interfaces, SizeIs(5)); // INTERFACE_NAME + 4 standard interfaces
signalReceived = true;
};
m_adaptor->emitInterfacesRemovedSignal();
ASSERT_TRUE(waitUntil(signalReceived));
}

View File

@ -1,92 +0,0 @@
/**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Connection_test.cpp
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
// Own
#include "defs.h"
// sdbus
#include <sdbus-c++/Error.h>
#include <sdbus-c++/IConnection.h>
// gmock
#include <gtest/gtest.h>
#include <gmock/gmock.h>
// STL
#include <thread>
using ::testing::Eq;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST(Connection, CanBeDefaultConstructed)
{
ASSERT_NO_THROW(auto con = sdbus::createConnection());
}
TEST(Connection, CanRequestRegisteredDbusName)
{
auto connection = sdbus::createConnection();
ASSERT_NO_THROW(connection->requestName(INTERFACE_NAME));
connection->releaseName(INTERFACE_NAME);
}
TEST(Connection, CannotRequestNonregisteredDbusName)
{
auto connection = sdbus::createConnection();
ASSERT_THROW(connection->requestName("some.random.not.supported.dbus.name"), sdbus::Error);
}
TEST(Connection, CanReleasedRequestedName)
{
auto connection = sdbus::createConnection();
connection->requestName(INTERFACE_NAME);
ASSERT_NO_THROW(connection->releaseName(INTERFACE_NAME));
}
TEST(Connection, CannotReleaseNonrequestedName)
{
auto connection = sdbus::createConnection();
ASSERT_THROW(connection->releaseName("some.random.nonrequested.name"), sdbus::Error);
}
TEST(Connection, CanEnterAndLeaveEventLoop)
{
auto connection = sdbus::createConnection();
connection->requestName(INTERFACE_NAME);
std::thread t([&](){ connection->enterEventLoop(); });
connection->leaveEventLoop();
t.join();
connection->releaseName(INTERFACE_NAME);
}

View File

@ -0,0 +1,240 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file DBusAsyncMethodsTests.cpp
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include "TestFixture.h"
#include "TestAdaptor.h"
#include "TestProxy.h"
#include "sdbus-c++/sdbus-c++.h"
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <string>
#include <thread>
#include <tuple>
#include <chrono>
#include <fstream>
#include <future>
#include <unistd.h>
using ::testing::Eq;
using ::testing::DoubleEq;
using ::testing::Gt;
using ::testing::Le;
using ::testing::AnyOf;
using ::testing::ElementsAre;
using ::testing::SizeIs;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTimesOut)
{
std::chrono::time_point<std::chrono::steady_clock> start;
try
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
{
if (err == nullptr)
promise.set_value(res);
else
promise.set_exception(std::make_exception_ptr(*err));
});
start = std::chrono::steady_clock::now();
m_proxy->doOperationClientSideAsyncWithTimeout(1us, 1000); // The operation will take 1s, but the timeout is 500ms, so we should time out
future.get();
FAIL() << "Expected sdbus::Error exception";
}
catch (const sdbus::Error& e)
{
ASSERT_THAT(e.getName(), AnyOf("org.freedesktop.DBus.Error.Timeout", "org.freedesktop.DBus.Error.NoReply"));
ASSERT_THAT(e.getMessage(), AnyOf("Connection timed out", "Method call timed out"));
auto measuredTimeout = std::chrono::steady_clock::now() - start;
ASSERT_THAT(measuredTimeout, Le(50ms));
}
catch(...)
{
FAIL() << "Expected sdbus::Error exception";
}
}
TEST_F(SdbusTestObject, RunsServerSideAsynchoronousMethodAsynchronously)
{
// Yeah, this is kinda timing-dependent test, but times should be safe...
std::mutex mtx;
std::vector<uint32_t> results;
std::atomic<bool> invoke{};
std::atomic<int> startedCount{};
auto call = [&](uint32_t param)
{
TestProxy proxy{BUS_NAME, OBJECT_PATH};
++startedCount;
while (!invoke) ;
auto result = proxy.doOperationAsync(param);
std::lock_guard<std::mutex> guard(mtx);
results.push_back(result);
};
std::thread invocations[]{std::thread{call, 1500}, std::thread{call, 1000}, std::thread{call, 500}};
while (startedCount != 3) ;
invoke = true;
std::for_each(std::begin(invocations), std::end(invocations), [](auto& t){ t.join(); });
ASSERT_THAT(results, ElementsAre(500, 1000, 1500));
}
TEST_F(SdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
{
std::atomic<size_t> resultCount{};
std::atomic<bool> invoke{};
std::atomic<int> startedCount{};
auto call = [&]()
{
TestProxy proxy{BUS_NAME, OBJECT_PATH};
++startedCount;
while (!invoke) ;
size_t localResultCount{};
for (size_t i = 0; i < 500; ++i)
{
auto result = proxy.doOperationAsync(i % 2);
if (result == (i % 2)) // Correct return value?
localResultCount++;
}
resultCount += localResultCount;
};
std::thread invocations[]{std::thread{call}, std::thread{call}, std::thread{call}};
while (startedCount != 3) ;
invoke = true;
std::for_each(std::begin(invocations), std::end(invocations), [](auto& t){ t.join(); });
ASSERT_THAT(resultCount, Eq(1500));
}
TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSide)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
{
if (err == nullptr)
promise.set_value(res);
else
promise.set_exception(std::make_exception_ptr(*err));
});
m_proxy->doOperationClientSideAsync(100);
ASSERT_THAT(future.get(), Eq(100));
}
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsPendingIfItIsInProgress)
{
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){});
auto call = m_proxy->doOperationClientSideAsync(100);
ASSERT_TRUE(call.isPending());
}
TEST_F(SdbusTestObject, CancelsPendingAsyncCallOnClientSide)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
auto call = m_proxy->doOperationClientSideAsync(100);
call.cancel();
ASSERT_THAT(future.wait_for(300ms), Eq(std::future_status::timeout));
}
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCancelled)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
auto call = m_proxy->doOperationClientSideAsync(100);
call.cancel();
ASSERT_FALSE(call.isPending());
}
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCompleted)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
auto call = m_proxy->doOperationClientSideAsync(0);
(void) future.get(); // Wait for the call to finish
ASSERT_TRUE(waitUntil([&call](){ return !call.isPending(); }));
}
TEST_F(SdbusTestObject, AnswersThatDefaultConstructedAsyncCallIsNotPending)
{
sdbus::PendingAsyncCall call;
ASSERT_FALSE(call.isPending());
}
TEST_F(SdbusTestObject, SupportsAsyncCallCopyAssignment)
{
sdbus::PendingAsyncCall call;
call = m_proxy->doOperationClientSideAsync(100);
ASSERT_TRUE(call.isPending());
}
TEST_F(SdbusTestObject, InvokesErroneousMethodAsynchronouslyOnClientSide)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
{
if (err == nullptr)
promise.set_value(res);
else
promise.set_exception(std::make_exception_ptr(*err));
});
m_proxy->doErroneousOperationClientSideAsync();
ASSERT_THROW(future.get(), sdbus::Error);
}

View File

@ -0,0 +1,133 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file DBusConnectionTests.cpp
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
// Own
#include "Defs.h"
// sdbus
#include <sdbus-c++/Error.h>
#include <sdbus-c++/IConnection.h>
// gmock
#include <gtest/gtest.h>
#include <gmock/gmock.h>
// STL
#include <thread>
#include <chrono>
using ::testing::Eq;
using namespace sdbus::test;
using namespace std::chrono_literals;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST(Connection, CanBeDefaultConstructed)
{
ASSERT_NO_THROW(auto con = sdbus::createConnection());
}
TEST(Connection, CanRequestRegisteredDbusName)
{
auto connection = sdbus::createConnection();
ASSERT_NO_THROW(connection->requestName(BUS_NAME))
<< "Perhaps you've forgotten to copy `org.sdbuscpp.integrationtests.conf` file to `/etc/dbus-1/system.d` directory before running the tests?";
}
TEST(Connection, CannotRequestNonregisteredDbusName)
{
auto connection = sdbus::createConnection();
ASSERT_THROW(connection->requestName("some.random.not.supported.dbus.name"), sdbus::Error);
}
TEST(Connection, CanReleasedRequestedName)
{
auto connection = sdbus::createConnection();
connection->requestName(BUS_NAME);
ASSERT_NO_THROW(connection->releaseName(BUS_NAME));
}
TEST(Connection, CannotReleaseNonrequestedName)
{
auto connection = sdbus::createConnection();
ASSERT_THROW(connection->releaseName("some.random.nonrequested.name"), sdbus::Error);
}
TEST(Connection, CanEnterAndLeaveEventLoop)
{
auto connection = sdbus::createConnection();
connection->requestName(BUS_NAME);
std::thread t([&](){ connection->enterEventLoop(); });
connection->leaveEventLoop();
t.join();
}
TEST(Connection, PollDataGetZeroTimeout)
{
sdbus::IConnection::PollData pd{};
pd.timeout_usec = 0;
ASSERT_TRUE(pd.getRelativeTimeout().has_value());
EXPECT_THAT(pd.getRelativeTimeout().value(), Eq(std::chrono::microseconds::zero()));
EXPECT_THAT(pd.getPollTimeout(), Eq(0));
}
TEST(Connection, PollDataGetInfiniteTimeout)
{
sdbus::IConnection::PollData pd{};
pd.timeout_usec = UINT64_MAX;
ASSERT_FALSE(pd.getRelativeTimeout().has_value());
EXPECT_THAT(pd.getPollTimeout(), Eq(-1));
}
TEST(Connection, PollDataGetZeroRelativeTimeoutForPast)
{
sdbus::IConnection::PollData pd{};
auto past = std::chrono::steady_clock::now() - 10s;
pd.timeout_usec = std::chrono::duration_cast<std::chrono::microseconds>(past.time_since_epoch()).count();
ASSERT_TRUE(pd.getRelativeTimeout().has_value());
EXPECT_THAT(pd.getRelativeTimeout().value(), Eq(0us));
EXPECT_THAT(pd.getPollTimeout(), Eq(0));
}
TEST(Connection, PollDataGetRelativeTimeoutInTolerance)
{
sdbus::IConnection::PollData pd{};
constexpr auto TIMEOUT = 1s;
constexpr auto TOLERANCE = 100ms;
auto future = std::chrono::steady_clock::now() + TIMEOUT;
pd.timeout_usec = std::chrono::duration_cast<std::chrono::microseconds>(future.time_since_epoch()).count();
ASSERT_TRUE(pd.getRelativeTimeout().has_value());
EXPECT_GE(pd.getRelativeTimeout().value(), TIMEOUT - TOLERANCE);
EXPECT_LE(pd.getRelativeTimeout().value(), TIMEOUT + TOLERANCE);
EXPECT_GE(pd.getPollTimeout(), 900);
EXPECT_LE(pd.getPollTimeout(), 1100);
}

View File

@ -0,0 +1,132 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file DBusGeneralTests.cpp
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include "TestAdaptor.h"
#include "TestProxy.h"
#include "TestFixture.h"
#include "sdbus-c++/sdbus-c++.h"
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <string>
#include <thread>
#include <tuple>
#include <chrono>
#include <fstream>
#include <future>
#include <unistd.h>
using ::testing::ElementsAre;
using ::testing::Eq;
using namespace std::chrono_literals;
using namespace sdbus::test;
using AConnection = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST(AdaptorAndProxy, CanBeConstructedSuccesfully)
{
auto connection = sdbus::createConnection();
connection->requestName(BUS_NAME);
ASSERT_NO_THROW(TestAdaptor adaptor(*connection, OBJECT_PATH));
ASSERT_NO_THROW(TestProxy proxy(BUS_NAME, OBJECT_PATH));
}
TEST_F(AConnection, WillCallCallbackHandlerForIncomingMessageMatchingMatchRule)
{
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic<bool> matchingMessageReceived{false};
auto slot = s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
{
if(msg.getPath() == OBJECT_PATH)
matchingMessageReceived = true;
});
m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(matchingMessageReceived));
}
TEST_F(AConnection, WillUnsubscribeMatchRuleWhenClientDestroysTheAssociatedSlot)
{
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic<bool> matchingMessageReceived{false};
auto slot = s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
{
if(msg.getPath() == OBJECT_PATH)
matchingMessageReceived = true;
});
slot.reset();
m_adaptor->emitSimpleSignal();
ASSERT_FALSE(waitUntil(matchingMessageReceived, 2s));
}
TEST_F(AConnection, CanAddFloatingMatchRule)
{
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic<bool> matchingMessageReceived{false};
auto con = sdbus::createSystemBusConnection();
con->enterEventLoopAsync();
auto callback = [&](sdbus::Message& msg)
{
if(msg.getPath() == OBJECT_PATH)
matchingMessageReceived = true;
};
con->addMatch(matchRule, std::move(callback), sdbus::floating_slot);
m_adaptor->emitSimpleSignal();
assert(waitUntil(matchingMessageReceived, 2s));
matchingMessageReceived = false;
con.reset();
m_adaptor->emitSimpleSignal();
ASSERT_FALSE(waitUntil(matchingMessageReceived, 2s));
}
TEST_F(AConnection, WillNotPassToMatchCallbackMessagesThatDoNotMatchTheRule)
{
auto matchRule = "type='signal',interface='" + INTERFACE_NAME + "',member='simpleSignal'";
std::atomic<size_t> numberOfMatchingMessages{};
auto slot = s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
{
if(msg.getMemberName() == "simpleSignal")
numberOfMatchingMessages++;
});
auto adaptor2 = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH_2);
m_adaptor->emitSignalWithMap({});
adaptor2->emitSimpleSignal();
m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil([&](){ return numberOfMatchingMessages == 2; }));
ASSERT_FALSE(waitUntil([&](){ return numberOfMatchingMessages > 2; }, 1s));
}

View File

@ -0,0 +1,274 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file DBusMethodsTests.cpp
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include "TestFixture.h"
#include "TestAdaptor.h"
#include "TestProxy.h"
#include "sdbus-c++/sdbus-c++.h"
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <string>
#include <thread>
#include <tuple>
#include <chrono>
#include <fstream>
#include <future>
#include <unistd.h>
using ::testing::Eq;
using ::testing::DoubleEq;
using ::testing::Gt;
using ::testing::Le;
using ::testing::AnyOf;
using ::testing::ElementsAre;
using ::testing::SizeIs;
using ::testing::NotNull;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST_F(SdbusTestObject, CallsEmptyMethodSuccesfully)
{
ASSERT_NO_THROW(m_proxy->noArgNoReturn());
}
TEST_F(SdbusTestObject, CallsMethodsWithBaseTypesSuccesfully)
{
auto resInt = m_proxy->getInt();
ASSERT_THAT(resInt, Eq(INT32_VALUE));
auto multiplyRes = m_proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
ASSERT_THAT(multiplyRes, Eq(INT64_VALUE * DOUBLE_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodsWithTuplesSuccesfully)
{
auto resTuple = m_proxy->getTuple();
ASSERT_THAT(std::get<0>(resTuple), Eq(UINT32_VALUE));
ASSERT_THAT(std::get<1>(resTuple), Eq(STRING_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodsWithStructSuccesfully)
{
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> a{};
auto vectorRes = m_proxy->getInts16FromStruct(a);
ASSERT_THAT(vectorRes, Eq(std::vector<int16_t>{0})); // because second item is by default initialized to 0
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> b{
UINT8_VALUE, INT16_VALUE, DOUBLE_VALUE, STRING_VALUE, {INT16_VALUE, -INT16_VALUE}
};
vectorRes = m_proxy->getInts16FromStruct(b);
ASSERT_THAT(vectorRes, Eq(std::vector<int16_t>{INT16_VALUE, INT16_VALUE, -INT16_VALUE}));
}
TEST_F(SdbusTestObject, CallsMethodWithVariantSuccesfully)
{
sdbus::Variant v{DOUBLE_VALUE};
auto variantRes = m_proxy->processVariant(v);
ASSERT_THAT(variantRes.get<int32_t>(), Eq(static_cast<int32_t>(DOUBLE_VALUE)));
}
TEST_F(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccesfully)
{
std::vector<int32_t> x{-2, 0, 2};
sdbus::Struct<sdbus::Variant, sdbus::Variant> y{false, true};
auto mapOfVariants = m_proxy->getMapOfVariants(x, y);
decltype(mapOfVariants) res{{-2, false}, {0, false}, {2, true}};
ASSERT_THAT(mapOfVariants[-2].get<bool>(), Eq(res[-2].get<bool>()));
ASSERT_THAT(mapOfVariants[0].get<bool>(), Eq(res[0].get<bool>()));
ASSERT_THAT(mapOfVariants[2].get<bool>(), Eq(res[2].get<bool>()));
}
TEST_F(SdbusTestObject, CallsMethodWithStructInStructSuccesfully)
{
auto val = m_proxy->getStructInStruct();
ASSERT_THAT(val.get<0>(), Eq(STRING_VALUE));
ASSERT_THAT(std::get<0>(std::get<1>(val))[INT32_VALUE], Eq(INT32_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithTwoStructsSuccesfully)
{
auto val = m_proxy->sumStructItems({1, 2}, {3, 4});
ASSERT_THAT(val, Eq(1 + 2 + 3 + 4));
}
TEST_F(SdbusTestObject, CallsMethodWithTwoVectorsSuccesfully)
{
auto val = m_proxy->sumVectorItems({1, 7}, {2, 3});
ASSERT_THAT(val, Eq(1 + 7 + 2 + 3));
}
TEST_F(SdbusTestObject, CallsMethodWithSignatureSuccesfully)
{
auto resSignature = m_proxy->getSignature();
ASSERT_THAT(resSignature, Eq(SIGNATURE_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithObjectPathSuccesfully)
{
auto resObjectPath = m_proxy->getObjPath();
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();
ASSERT_THAT(resComplex.count(0), Eq(1));
}
TEST_F(SdbusTestObject, CallsMultiplyMethodWithNoReplyFlag)
{
m_proxy->multiplyWithNoReply(INT64_VALUE, DOUBLE_VALUE);
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->doOperationWithTimeout(500ms, 20); // The operation will take 20ms, but the timeout is 500ms, so we are fine
ASSERT_THAT(res, Eq(20));
}
TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenMethodTimesOut)
{
auto start = std::chrono::steady_clock::now();
try
{
m_proxy->doOperationWithTimeout(1us, 1000); // The operation will take 1s, but the timeout is 1us, so we should time out
FAIL() << "Expected sdbus::Error exception";
}
catch (const sdbus::Error& e)
{
ASSERT_THAT(e.getName(), AnyOf("org.freedesktop.DBus.Error.Timeout", "org.freedesktop.DBus.Error.NoReply"));
ASSERT_THAT(e.getMessage(), AnyOf("Connection timed out", "Method call timed out"));
auto measuredTimeout = std::chrono::steady_clock::now() - start;
ASSERT_THAT(measuredTimeout, Le(50ms));
}
catch(...)
{
FAIL() << "Expected sdbus::Error exception";
}
}
TEST_F(SdbusTestObject, CallsMethodThatThrowsError)
{
try
{
m_proxy->throwError();
FAIL() << "Expected sdbus::Error exception";
}
catch (const sdbus::Error& e)
{
ASSERT_THAT(e.getName(), Eq("org.freedesktop.DBus.Error.AccessDenied"));
ASSERT_THAT(e.getMessage(), Eq("A test error occurred (Operation not permitted)"));
}
catch(...)
{
FAIL() << "Expected sdbus::Error exception";
}
}
TEST_F(SdbusTestObject, CallsErrorThrowingMethodWithDontExpectReplySet)
{
ASSERT_NO_THROW(m_proxy->throwErrorWithNoReply());
ASSERT_TRUE(waitUntil(m_adaptor->m_wasThrowErrorCalled));
}
TEST_F(SdbusTestObject, FailsCallingNonexistentMethod)
{
ASSERT_THROW(m_proxy->callNonexistentMethod(), sdbus::Error);
}
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentInterface)
{
ASSERT_THROW(m_proxy->callMethodOnNonexistentInterface(), sdbus::Error);
}
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentDestination)
{
TestProxy proxy("sdbuscpp.destination.that.does.not.exist", OBJECT_PATH);
ASSERT_THROW(proxy.getInt(), sdbus::Error);
}
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentObject)
{
TestProxy proxy(BUS_NAME, "/sdbuscpp/path/that/does/not/exist");
ASSERT_THROW(proxy.getInt(), sdbus::Error);
}
TEST_F(SdbusTestObject, CanReceiveSignalWhileMakingMethodCall)
{
m_proxy->emitTwoSimpleSignals();
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithMap));
}
TEST_F(SdbusTestObject, CanAccessAssociatedMethodCallMessageInMethodCallHandler)
{
m_proxy->doOperation(10); // This will save pointer to method call message on server side
ASSERT_THAT(m_adaptor->m_methodCallMsg, NotNull());
ASSERT_THAT(m_adaptor->m_methodCallMemberName, Eq("doOperation"));
}
TEST_F(SdbusTestObject, CanAccessAssociatedMethodCallMessageInAsyncMethodCallHandler)
{
m_proxy->doOperationAsync(10); // This will save pointer to method call message on server side
ASSERT_THAT(m_adaptor->m_methodCallMsg, NotNull());
ASSERT_THAT(m_adaptor->m_methodCallMemberName, Eq("doOperationAsync"));
}
#if LIBSYSTEMD_VERSION>=240
TEST_F(SdbusTestObject, CanSetGeneralMethodTimeoutWithLibsystemdVersionGreaterThan239)
{
s_adaptorConnection->setMethodCallTimeout(5000000);
ASSERT_THAT(s_adaptorConnection->getMethodCallTimeout(), Eq(5000000));
}
#else
TEST_F(SdbusTestObject, CannotSetGeneralMethodTimeoutWithLibsystemdVersionLessThan240)
{
ASSERT_THROW(s_adaptorConnection->setMethodCallTimeout(5000000), sdbus::Error);
ASSERT_THROW(s_adaptorConnection->getMethodCallTimeout(), sdbus::Error);
}
#endif

View File

@ -0,0 +1,85 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file DBusPropertiesTests.cpp
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include "TestFixture.h"
#include "TestAdaptor.h"
#include "TestProxy.h"
#include "sdbus-c++/sdbus-c++.h"
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <string>
#include <thread>
#include <tuple>
#include <chrono>
#include <fstream>
#include <future>
#include <unistd.h>
using ::testing::Eq;
using ::testing::DoubleEq;
using ::testing::Gt;
using ::testing::AnyOf;
using ::testing::ElementsAre;
using ::testing::SizeIs;
using ::testing::NotNull;
using ::testing::Not;
using ::testing::IsEmpty;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST_F(SdbusTestObject, ReadsReadOnlyPropertySuccesfully)
{
ASSERT_THAT(m_proxy->state(), Eq(DEFAULT_STATE_VALUE));
}
TEST_F(SdbusTestObject, FailsWritingToReadOnlyProperty)
{
ASSERT_THROW(m_proxy->setStateProperty("new_value"), sdbus::Error);
}
TEST_F(SdbusTestObject, WritesAndReadsReadWritePropertySuccesfully)
{
uint32_t newActionValue = 5678;
m_proxy->action(newActionValue);
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
}
TEST_F(SdbusTestObject, CanAccessAssociatedPropertySetMessageInPropertySetHandler)
{
m_proxy->blocking(true); // This will save pointer to property get message on server side
ASSERT_THAT(m_adaptor->m_propertySetMsg, NotNull());
ASSERT_THAT(m_adaptor->m_propertySetSender, Not(IsEmpty()));
}

View File

@ -0,0 +1,157 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file AdaptorAndProxy_test.cpp
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include "TestFixture.h"
#include "TestAdaptor.h"
#include "TestProxy.h"
#include "sdbus-c++/sdbus-c++.h"
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <string>
#include <chrono>
using ::testing::Eq;
using ::testing::DoubleEq;
using ::testing::Gt;
using ::testing::AnyOf;
using ::testing::ElementsAre;
using ::testing::SizeIs;
using ::testing::NotNull;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST_F(SdbusTestObject, EmitsSimpleSignalSuccesfully)
{
m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
}
TEST_F(SdbusTestObject, EmitsSimpleSignalToMultipleProxiesSuccesfully)
{
auto proxy1 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
auto proxy2 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(proxy1->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(proxy2->m_gotSimpleSignal));
}
TEST_F(SdbusTestObject, ProxyDoesNotReceiveSignalFromOtherBusName)
{
auto otherBusName = BUS_NAME + "2";
auto connection2 = sdbus::createConnection(otherBusName);
auto adaptor2 = std::make_unique<TestAdaptor>(*connection2, OBJECT_PATH);
adaptor2->emitSimpleSignal();
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
}
TEST_F(SdbusTestObject, EmitsSignalWithMapSuccesfully)
{
m_adaptor->emitSignalWithMap({{0, "zero"}, {1, "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->emitSignalWithVariant(d);
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithVariant));
ASSERT_THAT(m_proxy->m_variantFromSignal, DoubleEq(d));
}
TEST_F(SdbusTestObject, EmitsSignalWithoutRegistrationSuccesfully)
{
m_adaptor->emitSignalWithoutRegistration({"platform", {"av"}});
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithSignature));
ASSERT_THAT(m_proxy->m_signatureFromSignal["platform"], Eq("av"));
}
TEST_F(SdbusTestObject, CanAccessAssociatedSignalMessageInSignalHandler)
{
m_adaptor->emitSimpleSignal();
waitUntil(m_proxy->m_gotSimpleSignal);
ASSERT_THAT(m_proxy->m_signalMsg, NotNull());
ASSERT_THAT(m_proxy->m_signalMemberName, Eq("simpleSignal"));
}
TEST_F(SdbusTestObject, UnregistersSignalHandler)
{
ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler());
m_adaptor->emitSimpleSignal();
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
}
TEST_F(SdbusTestObject, UnregistersSignalHandlerForSomeProxies)
{
auto proxy1 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
auto proxy2 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler());
m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(proxy1->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(proxy2->m_gotSimpleSignal));
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
}
TEST_F(SdbusTestObject, ReRegistersSignalHandler)
{
// unregister simple-signal handler
ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler());
m_adaptor->emitSimpleSignal();
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
// re-register simple-signal handler
ASSERT_NO_THROW(m_proxy->reRegisterSimpleSignalHandler());
m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
}

View File

@ -0,0 +1,258 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file DBusStandardInterfacesTests.cpp
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include "TestFixture.h"
#include "TestAdaptor.h"
#include "TestProxy.h"
#include "sdbus-c++/sdbus-c++.h"
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <string>
#include <thread>
#include <tuple>
#include <chrono>
#include <fstream>
#include <future>
#include <unistd.h>
using ::testing::Eq;
using ::testing::DoubleEq;
using ::testing::Gt;
using ::testing::AnyOf;
using ::testing::ElementsAre;
using ::testing::SizeIs;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST_F(SdbusTestObject, PingsViaPeerInterface)
{
ASSERT_NO_THROW(m_proxy->Ping());
}
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());
}
// TODO: Adjust expected xml and uncomment this test
//TEST_F(SdbusTestObject, AnswersXmlApiDescriptionViaIntrospectableInterface)
//{
// ASSERT_THAT(m_proxy->Introspect(), Eq(m_adaptor->getExpectedXmlApiDescription()));
//}
TEST_F(SdbusTestObject, GetsPropertyViaPropertiesInterface)
{
ASSERT_THAT(m_proxy->Get(INTERFACE_NAME, "state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
}
TEST_F(SdbusTestObject, SetsPropertyViaPropertiesInterface)
{
uint32_t newActionValue = 2345;
m_proxy->Set(INTERFACE_NAME, "action", newActionValue);
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
}
TEST_F(SdbusTestObject, GetsAllPropertiesViaPropertiesInterface)
{
const auto properties = m_proxy->GetAll(INTERFACE_NAME);
ASSERT_THAT(properties, SizeIs(3));
EXPECT_THAT(properties.at("state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
EXPECT_THAT(properties.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(properties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
}
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*/ )
{
EXPECT_THAT(interfaceName, Eq(INTERFACE_NAME));
EXPECT_THAT(changedProperties, SizeIs(1));
EXPECT_THAT(changedProperties.at("blocking").get<bool>(), Eq(!DEFAULT_BLOCKING_VALUE));
signalReceived = true;
};
m_proxy->blocking(!DEFAULT_BLOCKING_VALUE);
m_proxy->action(DEFAULT_ACTION_VALUE*2);
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME, {"blocking"});
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForAllProperties)
{
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 )
{
EXPECT_THAT(interfaceName, Eq(INTERFACE_NAME));
EXPECT_THAT(changedProperties, SizeIs(1));
EXPECT_THAT(changedProperties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
ASSERT_THAT(invalidatedProperties, SizeIs(1));
EXPECT_THAT(invalidatedProperties[0], Eq("action"));
signalReceived = true;
};
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME);
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, GetsZeroManagedObjectsIfHasNoSubPathObjects)
{
m_adaptor.reset();
const auto objectsInterfacesAndProperties = m_objectManagerProxy->GetManagedObjects();
ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(0));
}
TEST_F(SdbusTestObject, GetsManagedObjectsSuccessfully)
{
auto adaptor2 = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH_2);
const auto objectsInterfacesAndProperties = m_objectManagerProxy->GetManagedObjects();
ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(2));
EXPECT_THAT(objectsInterfacesAndProperties.at(OBJECT_PATH)
.at(org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME)
.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(objectsInterfacesAndProperties.at(OBJECT_PATH_2)
.at(org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME)
.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
}
TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
EXPECT_THAT(interfacesAndProperties, SizeIs(1));
EXPECT_THAT(interfacesAndProperties.count(INTERFACE_NAME), Eq(1));
#if LIBSYSTEMD_VERSION<=244
// Up to sd-bus v244, all properties are added to the list, i.e. `state', `action', and `blocking' in this case.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("action"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
#else
// Since v245 sd-bus does not add to the InterfacesAdded signal message the values of properties marked only
// for invalidation on change, which makes the behavior consistent with the PropertiesChangedSignal.
// So in this specific instance, `action' property is no more added to the list.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(2));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
#endif
signalReceived = true;
};
m_adaptor->emitInterfacesAddedSignal({INTERFACE_NAME});
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
EXPECT_THAT(interfacesAndProperties, SizeIs(5)); // INTERFACE_NAME + 4 standard interfaces
#if LIBSYSTEMD_VERSION<=244
// Up to sd-bus v244, all properties are added to the list, i.e. `state', `action', and `blocking' in this case.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("action"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
#else
// Since v245 sd-bus does not add to the InterfacesAdded signal message the values of properties marked only
// for invalidation on change, which makes the behavior consistent with the PropertiesChangedSignal.
// So in this specific instance, `action' property is no more added to the list.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(2));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
#endif
signalReceived = true;
};
m_adaptor->emitInterfacesAddedSignal();
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
ASSERT_THAT(interfaces, SizeIs(1));
EXPECT_THAT(interfaces[0], Eq(INTERFACE_NAME));
signalReceived = true;
};
m_adaptor->emitInterfacesRemovedSignal({INTERFACE_NAME});
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
ASSERT_THAT(interfaces, SizeIs(5)); // INTERFACE_NAME + 4 standard interfaces
signalReceived = true;
};
m_adaptor->emitInterfacesRemovedSignal();
ASSERT_TRUE(waitUntil(signalReceived));
}

View File

@ -1,8 +1,8 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file defs.h * @file Defs.h
* *
* Created on: Jan 2, 2017 * Created on: Jan 2, 2017
* Project: sdbus-c++ * Project: sdbus-c++
@ -28,9 +28,16 @@
#define SDBUS_CPP_INTEGRATIONTESTS_DEFS_H_ #define SDBUS_CPP_INTEGRATIONTESTS_DEFS_H_
#include "sdbus-c++/Types.h" #include "sdbus-c++/Types.h"
#include <chrono>
#include <ostream>
namespace sdbus { namespace test {
const std::string INTERFACE_NAME{"org.sdbuscpp.integrationtests"}; const std::string INTERFACE_NAME{"org.sdbuscpp.integrationtests"};
const std::string OBJECT_PATH{"/"}; const std::string BUS_NAME = INTERFACE_NAME;
const std::string MANAGER_PATH {"/org/sdbuscpp/integrationtests"};
const std::string OBJECT_PATH {"/org/sdbuscpp/integrationtests/ObjectA1"};
const std::string OBJECT_PATH_2{"/org/sdbuscpp/integrationtests/ObjectB1"};
constexpr const uint8_t UINT8_VALUE{1}; constexpr const uint8_t UINT8_VALUE{1};
constexpr const int16_t INT16_VALUE{21}; constexpr const int16_t INT16_VALUE{21};
@ -49,4 +56,18 @@ const bool DEFAULT_BLOCKING_VALUE{true};
constexpr const double DOUBLE_VALUE{3.24L}; constexpr const double DOUBLE_VALUE{3.24L};
}}
namespace testing::internal {
// Printer for std::chrono::duration types.
// This is a workaround, since it's not a good thing to add this to std namespace.
template< class Rep, class Period >
void PrintTo(const ::std::chrono::duration<Rep, Period>& d, ::std::ostream* os) {
auto seconds = std::chrono::duration_cast<std::chrono::duration<double>>(d);
*os << seconds.count() << "s";
}
}
#endif /* SDBUS_CPP_INTEGRATIONTESTS_DEFS_H_ */ #endif /* SDBUS_CPP_INTEGRATIONTESTS_DEFS_H_ */

View File

@ -0,0 +1,426 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.cpp
*
* Created on: May 23, 2020
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include "TestAdaptor.h"
#include <thread>
#include <chrono>
#include <atomic>
namespace sdbus { namespace test {
TestAdaptor::TestAdaptor(sdbus::IConnection& connection, const std::string& path) :
AdaptorInterfaces(connection, path)
{
registerAdaptor();
}
TestAdaptor::~TestAdaptor()
{
unregisterAdaptor();
}
void TestAdaptor::noArgNoReturn()
{
}
int32_t TestAdaptor::getInt()
{
return INT32_VALUE;
}
std::tuple<uint32_t, std::string> TestAdaptor::getTuple()
{
return std::make_tuple(UINT32_VALUE, STRING_VALUE);
}
double TestAdaptor::multiply(const int64_t& a, const double& b)
{
return a * b;
}
void TestAdaptor::multiplyWithNoReply(const int64_t& a, const double& b)
{
m_multiplyResult = a * b;
m_wasMultiplyCalled = true;
}
std::vector<int16_t> TestAdaptor::getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x)
{
std::vector<int16_t> res{x.get<1>()};
auto y = std::get<std::vector<int16_t>>(x);
res.insert(res.end(), y.begin(), y.end());
return res;
}
sdbus::Variant TestAdaptor::processVariant(const sdbus::Variant& v)
{
sdbus::Variant res = static_cast<int32_t>(v.get<double>());
return res;
}
std::map<int32_t, sdbus::Variant> TestAdaptor::getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y)
{
std::map<int32_t, sdbus::Variant> res;
for (auto item : x)
{
res[item] = (item <= 0) ? std::get<0>(y) : std::get<1>(y);
}
return res;
}
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> TestAdaptor::getStructInStruct()
{
return sdbus::make_struct(STRING_VALUE, sdbus::make_struct(std::map<int32_t, int32_t>{{INT32_VALUE, INT32_VALUE}}));
}
int32_t TestAdaptor::sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b)
{
int32_t res{0};
res += std::get<0>(a) + std::get<1>(a);
res += std::get<0>(b) + std::get<1>(b);
return res;
}
uint32_t TestAdaptor::sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b)
{
uint32_t res{0};
for (auto x : a)
{
res += x;
}
for (auto x : b)
{
res += x;
}
return res;
}
uint32_t TestAdaptor::doOperation(const uint32_t& param)
{
std::this_thread::sleep_for(std::chrono::milliseconds(param));
m_methodCallMsg = getObject().getCurrentlyProcessedMessage();
m_methodCallMemberName = m_methodCallMsg->getMemberName();
return param;
}
void TestAdaptor::doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t param)
{
m_methodCallMsg = getObject().getCurrentlyProcessedMessage();
m_methodCallMemberName = m_methodCallMsg->getMemberName();
if (param == 0)
{
// Don't sleep and return the result from this thread
result.returnResults(param);
}
else
{
// Process asynchronously in another thread and return the result from there
std::thread([param, result = std::move(result)]()
{
std::this_thread::sleep_for(std::chrono::milliseconds(param));
result.returnResults(param);
}).detach();
}
}
sdbus::Signature TestAdaptor::getSignature()
{
return SIGNATURE_VALUE;
}
sdbus::ObjectPath TestAdaptor::getObjPath()
{
return OBJECT_PATH_VALUE;
}
sdbus::UnixFd TestAdaptor::getUnixFd()
{
return sdbus::UnixFd{UNIX_FD_VALUE};
}
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> TestAdaptor::getComplex()
{
return { // map
{
0, // uint_64_t
{ // struct
{ // map
{
23, // uint8_t
{ // vector
{ // struct
"/object/path", // object path
false,
Variant{3.14},
{ // map
{0, "zero"}
}
}
}
}
},
"a{t(a{ya(obva{is})}gs)}", // signature
std::string{}
}
}
};
}
void TestAdaptor::throwError()
{
m_wasThrowErrorCalled = true;
throw sdbus::createError(1, "A test error occurred");
}
void TestAdaptor::throwErrorWithNoReply()
{
TestAdaptor::throwError();
}
void TestAdaptor::doPrivilegedStuff()
{
// Intentionally left blank
}
void TestAdaptor::emitTwoSimpleSignals()
{
emitSimpleSignal();
emitSignalWithMap({});
}
std::string TestAdaptor::state()
{
return m_state;
}
uint32_t TestAdaptor::action()
{
return m_action;
}
void TestAdaptor::action(const uint32_t& value)
{
m_action = value;
}
bool TestAdaptor::blocking()
{
return m_blocking;
}
void TestAdaptor::blocking(const bool& value)
{
m_propertySetMsg = getObject().getCurrentlyProcessedMessage();
m_propertySetSender = m_propertySetMsg->getSender();
m_blocking = value;
}
void TestAdaptor::emitSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
{
getObject().emitSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).withArguments(s);
}
std::string TestAdaptor::getExpectedXmlApiDescription() const
{
return
R"delimiter(<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.DBus.Peer">
<method name="Ping"/>
<method name="GetMachineId">
<arg type="s" name="machine_uuid" direction="out"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" type="s" direction="out"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg name="interface" direction="in" type="s"/>
<arg name="property" direction="in" type="s"/>
<arg name="value" direction="out" type="v"/>
</method>
<method name="GetAll">
<arg name="interface" direction="in" type="s"/>
<arg name="properties" direction="out" type="a{sv}"/>
</method>
<method name="Set">
<arg name="interface" direction="in" type="s"/>
<arg name="property" direction="in" type="s"/>
<arg name="value" direction="in" type="v"/>
</method>
<signal name="PropertiesChanged">
<arg type="s" name="interface"/>
<arg type="a{sv}" name="changed_properties"/>
<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">
<arg type="u" direction="in"/>
<arg type="u" direction="out"/>
</method>
<method name="doOperationAsync">
<arg type="u" direction="in"/>
<arg type="u" direction="out"/>
</method>
<method name="doPrivilegedStuff">
<annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
</method>
<method name="emitTwoSimpleSignals">
</method>
<method name="getComplex">
<arg type="a{t(a{ya(obva{is})}gs)}" direction="out"/>
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
</method>
<method name="getInt">)delimiter"
#if LIBSYSTEMD_VERSION>=242
R"delimiter(
<arg type="i" name="anInt" direction="out"/>)delimiter"
#else
R"delimiter(
<arg type="i" direction="out"/>)delimiter"
#endif
R"delimiter(
</method>
<method name="getInts16FromStruct">
<arg type="(yndsan)" direction="in"/>
<arg type="an" direction="out"/>
</method>
<method name="getMapOfVariants">)delimiter"
#if LIBSYSTEMD_VERSION>=242
R"delimiter(
<arg type="ai" name="x" direction="in"/>
<arg type="(vv)" name="y" direction="in"/>
<arg type="a{iv}" name="aMapOfVariants" direction="out"/>)delimiter"
#else
R"delimiter(
<arg type="ai" direction="in"/>
<arg type="(vv)" direction="in"/>
<arg type="a{iv}" direction="out"/>)delimiter"
#endif
R"delimiter(
</method>
<method name="getObjPath">
<arg type="o" direction="out"/>
</method>
<method name="getSignature">
<arg type="g" direction="out"/>
</method>
<method name="getStructInStruct">
<arg type="(s(a{ii}))" direction="out"/>
</method>
<method name="getTuple">
<arg type="u" direction="out"/>
<arg type="s" direction="out"/>
</method>
<method name="getUnixFd">
<arg type="h" direction="out"/>
</method>
<method name="multiply">)delimiter"
#if LIBSYSTEMD_VERSION>=242
R"delimiter(
<arg type="x" name="a" direction="in"/>
<arg type="d" name="b" direction="in"/>
<arg type="d" name="result" direction="out"/>)delimiter"
#else
R"delimiter(
<arg type="x" direction="in"/>
<arg type="d" direction="in"/>
<arg type="d" direction="out"/>)delimiter"
#endif
R"delimiter(
</method>
<method name="multiplyWithNoReply">
<arg type="x" direction="in"/>
<arg type="d" direction="in"/>
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="noArgNoReturn">
</method>
<method name="processVariant">
<arg type="v" direction="in"/>
<arg type="v" direction="out"/>
</method>
<method name="sumStructItems">
<arg type="(yq)" direction="in"/>
<arg type="(ix)" direction="in"/>
<arg type="i" direction="out"/>
</method>
<method name="sumVectorItems">
<arg type="aq" direction="in"/>
<arg type="at" direction="in"/>
<arg type="u" direction="out"/>
</method>
<method name="throwError">
</method>
<method name="throwErrorWithNoReply">
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<signal name="signalWithMap">
<arg type="a{is}"/>
</signal>
<signal name="signalWithVariant">
<arg type="v"/>
</signal>
<signal name="simpleSignal">
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
</signal>
<property name="action" type="u" access="readwrite">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="invalidates"/>
</property>
<property name="blocking" type="b" access="readwrite">
</property>
<property name="state" type="s" access="read">
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
</property>
</interface>
</node>
)delimiter";
}
}}

View File

@ -0,0 +1,114 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.h
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SDBUS_CPP_INTEGRATIONTESTS_TESTADAPTOR_H_
#define SDBUS_CPP_INTEGRATIONTESTS_TESTADAPTOR_H_
#include "integrationtests-adaptor.h"
#include "Defs.h"
#include <thread>
#include <chrono>
#include <atomic>
#include <utility>
namespace sdbus { namespace test {
class ObjectManagerTestAdaptor final : public sdbus::AdaptorInterfaces< sdbus::ObjectManager_adaptor >
{
public:
ObjectManagerTestAdaptor(sdbus::IConnection& connection, std::string path) :
AdaptorInterfaces(connection, std::move(path))
{
registerAdaptor();
}
~ObjectManagerTestAdaptor()
{
unregisterAdaptor();
}
};
class TestAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::integrationtests_adaptor
, sdbus::Properties_adaptor
, sdbus::ManagedObject_adaptor >
{
public:
TestAdaptor(sdbus::IConnection& connection, const std::string& path);
~TestAdaptor();
protected:
void noArgNoReturn() override;
int32_t getInt() override;
std::tuple<uint32_t, std::string> getTuple() override;
double multiply(const int64_t& a, const double& b) override;
void multiplyWithNoReply(const int64_t& a, const double& b) override;
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0) override;
sdbus::Variant processVariant(const sdbus::Variant& variant) override;
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) override;
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() override;
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1) override;
uint32_t sumVectorItems(const std::vector<uint16_t>& arg0, const std::vector<uint64_t>& arg1) override;
uint32_t doOperation(const uint32_t& arg0) override;
void doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t arg0) override;
sdbus::Signature getSignature() override;
sdbus::ObjectPath getObjPath() override;
sdbus::UnixFd getUnixFd() override;
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() override;
void throwError() override;
void throwErrorWithNoReply() override;
void doPrivilegedStuff() override;
void emitTwoSimpleSignals() override;
uint32_t action() override;
void action(const uint32_t& value) override;
bool blocking() override;
void blocking(const bool& value) override;
std::string state() override;
public:
void emitSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s);
std::string getExpectedXmlApiDescription() const;
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_wasMultiplyCalled{false};
mutable double m_multiplyResult{};
mutable std::atomic<bool> m_wasThrowErrorCalled{false};
const Message* m_methodCallMsg{};
std::string m_methodCallMemberName;
const Message* m_propertySetMsg{};
std::string m_propertySetSender;
};
}}
#endif /* INTEGRATIONTESTS_TESTADAPTOR_H_ */

View File

@ -0,0 +1,34 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.cpp
*
* Created on: May 23, 2020
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include "TestFixture.h"
namespace sdbus { namespace test {
std::unique_ptr<sdbus::IConnection> TestFixture::s_adaptorConnection = sdbus::createSystemBusConnection();
std::unique_ptr<sdbus::IConnection> TestFixture::s_proxyConnection = sdbus::createSystemBusConnection();
}}

View File

@ -0,0 +1,111 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.h
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SDBUS_CPP_INTEGRATIONTESTS_TESTFIXTURE_H_
#define SDBUS_CPP_INTEGRATIONTESTS_TESTFIXTURE_H_
#include "TestAdaptor.h"
#include "TestProxy.h"
#include "Defs.h"
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <thread>
#include <chrono>
#include <atomic>
#include <chrono>
namespace sdbus { namespace test {
class TestFixture : public ::testing::Test
{
public:
static void SetUpTestCase()
{
s_proxyConnection->enterEventLoopAsync();
s_adaptorConnection->requestName(BUS_NAME);
s_adaptorConnection->enterEventLoopAsync();
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Give time for the proxy connection to start listening to signals
}
static void TearDownTestCase()
{
s_adaptorConnection->releaseName(BUS_NAME);
s_adaptorConnection->leaveEventLoop();
s_proxyConnection->leaveEventLoop();
}
template <typename _Fnc>
static bool waitUntil(_Fnc&& fnc, std::chrono::milliseconds timeout = std::chrono::seconds(5))
{
using namespace std::chrono_literals;
std::chrono::milliseconds elapsed{};
std::chrono::milliseconds step{5ms};
do {
std::this_thread::sleep_for(step);
elapsed += step;
if (elapsed > timeout)
return false;
} while (!fnc());
return true;
}
static bool waitUntil(std::atomic<bool>& flag, std::chrono::milliseconds timeout = std::chrono::seconds(5))
{
return waitUntil([&flag]() -> bool { return flag; }, timeout);
}
private:
void SetUp() override
{
m_objectManagerProxy = std::make_unique<ObjectManagerTestProxy>(*s_proxyConnection, BUS_NAME, MANAGER_PATH);
m_proxy = std::make_unique<TestProxy>(*s_proxyConnection, BUS_NAME, OBJECT_PATH);
m_objectManagerAdaptor = std::make_unique<ObjectManagerTestAdaptor>(*s_adaptorConnection, MANAGER_PATH);
m_adaptor = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH);
}
void TearDown() override
{
m_proxy.reset();
m_adaptor.reset();
}
public:
static std::unique_ptr<sdbus::IConnection> s_adaptorConnection;
static std::unique_ptr<sdbus::IConnection> s_proxyConnection;
std::unique_ptr<ObjectManagerTestAdaptor> m_objectManagerAdaptor;
std::unique_ptr<ObjectManagerTestProxy> m_objectManagerProxy;
std::unique_ptr<TestAdaptor> m_adaptor;
std::unique_ptr<TestProxy> m_proxy;
};
}}
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTFIXTURE_H_ */

View File

@ -0,0 +1,161 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.cpp
*
* Created on: May 23, 2020
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include "TestProxy.h"
#include <thread>
#include <chrono>
#include <atomic>
namespace sdbus { namespace test {
TestProxy::TestProxy(std::string destination, std::string objectPath)
: ProxyInterfaces(std::move(destination), std::move(objectPath))
{
getProxy().uponSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).call([this](const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s){ this->onSignalWithoutRegistration(s); });
registerProxy();
}
TestProxy::TestProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
{
getProxy().uponSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).call([this](const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s){ this->onSignalWithoutRegistration(s); });
registerProxy();
}
TestProxy::~TestProxy()
{
unregisterProxy();
}
void TestProxy::onSimpleSignal()
{
m_signalMsg = getProxy().getCurrentlyProcessedMessage();
m_signalMemberName = m_signalMsg->getMemberName();
m_gotSimpleSignal = true;
}
void TestProxy::onSignalWithMap(const std::map<int32_t, std::string>& aMap)
{
m_mapFromSignal = aMap;
m_gotSignalWithMap = true;
}
void TestProxy::onSignalWithVariant(const sdbus::Variant& aVariant)
{
m_variantFromSignal = aVariant.get<double>();
m_gotSignalWithVariant = true;
}
void TestProxy::onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
{
m_signatureFromSignal[std::get<0>(s)] = static_cast<std::string>(std::get<0>(std::get<1>(s)));
m_gotSignalWithSignature = true;
}
void TestProxy::onDoOperationReply(uint32_t returnValue, const sdbus::Error* error)
{
if (m_DoOperationClientSideAsyncReplyHandler)
m_DoOperationClientSideAsyncReplyHandler(returnValue, error);
}
void TestProxy::onPropertiesChanged( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties
, const std::vector<std::string>& invalidatedProperties )
{
if (m_onPropertiesChangedHandler)
m_onPropertiesChangedHandler(interfaceName, changedProperties, invalidatedProperties);
}
void TestProxy::installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, const sdbus::Error* err)> handler)
{
m_DoOperationClientSideAsyncReplyHandler = std::move(handler);
}
uint32_t TestProxy::doOperationWithTimeout(const std::chrono::microseconds &timeout, uint32_t param)
{
using namespace std::chrono_literals;
uint32_t result;
getProxy().callMethod("doOperation").onInterface(sdbus::test::INTERFACE_NAME).withTimeout(timeout).withArguments(param).storeResultsTo(result);
return result;
}
sdbus::PendingAsyncCall TestProxy::doOperationClientSideAsync(uint32_t param)
{
return getProxy().callMethodAsync("doOperation")
.onInterface(sdbus::test::INTERFACE_NAME)
.withArguments(param)
.uponReplyInvoke([this](const sdbus::Error* error, uint32_t returnValue)
{
this->onDoOperationReply(returnValue, error);
});
}
void TestProxy::doErroneousOperationClientSideAsync()
{
getProxy().callMethodAsync("throwError")
.onInterface(sdbus::test::INTERFACE_NAME)
.uponReplyInvoke([this](const sdbus::Error* error)
{
this->onDoOperationReply(0, error);
});
}
void TestProxy::doOperationClientSideAsyncWithTimeout(const std::chrono::microseconds &timeout, uint32_t param)
{
using namespace std::chrono_literals;
getProxy().callMethodAsync("doOperation")
.onInterface(sdbus::test::INTERFACE_NAME)
.withTimeout(timeout)
.withArguments(param)
.uponReplyInvoke([this](const sdbus::Error* error, uint32_t returnValue)
{
this->onDoOperationReply(returnValue, error);
});
}
int32_t TestProxy::callNonexistentMethod()
{
int32_t result;
getProxy().callMethod("callNonexistentMethod").onInterface(sdbus::test::INTERFACE_NAME).storeResultsTo(result);
return result;
}
int32_t TestProxy::callMethodOnNonexistentInterface()
{
int32_t result;
getProxy().callMethod("someMethod").onInterface("sdbuscpp.interface.that.does.not.exist").storeResultsTo(result);
return result;
}
void TestProxy::setStateProperty(const std::string& value)
{
getProxy().setProperty("state").onInterface(sdbus::test::INTERFACE_NAME).toValue(value);
}
}}

View File

@ -1,8 +1,8 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file TestingProxy.h * @file TestAdaptor.h
* *
* Created on: Jan 2, 2017 * Created on: Jan 2, 2017
* Project: sdbus-c++ * Project: sdbus-c++
@ -24,94 +24,81 @@
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>. * along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef SDBUS_CPP_INTEGRATIONTESTS_TESTINGPROXY_H_ #ifndef SDBUS_CPP_INTEGRATIONTESTS_TESTPROXY_H_
#define SDBUS_CPP_INTEGRATIONTESTS_TESTINGPROXY_H_ #define SDBUS_CPP_INTEGRATIONTESTS_TESTPROXY_H_
#include "proxy-glue.h" #include "integrationtests-proxy.h"
#include "Defs.h"
#include <thread>
#include <chrono>
#include <atomic> #include <atomic>
class TestingProxy : public sdbus::ProxyInterfaces< ::testing_proxy namespace sdbus { namespace test {
, sdbus::Peer_proxy
, sdbus::Introspectable_proxy class ObjectManagerTestProxy final : public sdbus::ProxyInterfaces< sdbus::ObjectManager_proxy >
, sdbus::Properties_proxy
, sdbus::ObjectManager_proxy >
{ {
public: public:
TestingProxy(std::string destination, std::string objectPath) ObjectManagerTestProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
: ProxyInterfaces(std::move(destination), std::move(objectPath))
{
registerProxy();
}
TestingProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath)) : ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
{ {
registerProxy(); registerProxy();
} }
~TestingProxy() ~ObjectManagerTestProxy()
{ {
unregisterProxy(); unregisterProxy();
} }
void installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, const sdbus::Error* err)> handler)
{
m_DoOperationClientSideAsyncReplyHandler = handler;
}
protected: protected:
void onSimpleSignal() override void onInterfacesAdded(const sdbus::ObjectPath& objectPath, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties) override
{
m_gotSimpleSignal = true;
}
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_variantFromSignal = v.get<double>();
m_gotSignalWithVariant = true;
}
void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s) override
{
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
{
if (m_DoOperationClientSideAsyncReplyHandler)
m_DoOperationClientSideAsyncReplyHandler(returnValue, error);
}
// Signals of standard D-Bus interfaces
void onPropertiesChanged( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties
, const std::vector<std::string>& invalidatedProperties ) override
{
if (m_onPropertiesChangedHandler)
m_onPropertiesChangedHandler(interfaceName, changedProperties, invalidatedProperties);
}
void onInterfacesAdded( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties) override
{ {
if (m_onInterfacesAddedHandler) if (m_onInterfacesAddedHandler)
m_onInterfacesAddedHandler(objectPath, interfacesAndProperties); m_onInterfacesAddedHandler(objectPath, interfacesAndProperties);
} }
void onInterfacesRemoved( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces) override void onInterfacesRemoved(const sdbus::ObjectPath& objectPath, const std::vector<std::string>& interfaces) override
{ {
if (m_onInterfacesRemovedHandler) if (m_onInterfacesRemovedHandler)
m_onInterfacesRemovedHandler(objectPath, interfaces); m_onInterfacesRemovedHandler(objectPath, interfaces);
} }
public: // for tests
std::function<void(const sdbus::ObjectPath&, const std::map<std::string, std::map<std::string, sdbus::Variant>>&)> m_onInterfacesAddedHandler;
std::function<void(const sdbus::ObjectPath&, const std::vector<std::string>&)> m_onInterfacesRemovedHandler;
};
class TestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integrationtests_proxy
, sdbus::Peer_proxy
, sdbus::Introspectable_proxy
, sdbus::Properties_proxy >
{
public:
TestProxy(std::string destination, std::string objectPath);
TestProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath);
~TestProxy();
protected:
void onSimpleSignal() override;
void onSignalWithMap(const std::map<int32_t, std::string>& aMap) override;
void onSignalWithVariant(const sdbus::Variant& aVariant) override;
void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s);
void onDoOperationReply(uint32_t returnValue, const sdbus::Error* error);
// Signals of standard D-Bus interfaces
void onPropertiesChanged( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties
, const std::vector<std::string>& invalidatedProperties ) override;
public:
void installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, const sdbus::Error* err)> handler);
uint32_t doOperationWithTimeout(const std::chrono::microseconds &timeout, uint32_t param);
sdbus::PendingAsyncCall doOperationClientSideAsync(uint32_t param);
void doErroneousOperationClientSideAsync();
void doOperationClientSideAsyncWithTimeout(const std::chrono::microseconds &timeout, uint32_t param);
int32_t callNonexistentMethod();
int32_t callMethodOnNonexistentInterface();
void setStateProperty(const std::string& value);
//private: //private:
public: // for tests public: // for tests
int m_SimpleSignals = 0; int m_SimpleSignals = 0;
@ -125,8 +112,11 @@ public: // for tests
std::function<void(uint32_t res, const sdbus::Error* err)> m_DoOperationClientSideAsyncReplyHandler; 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; std::function<void(const std::string&, const std::map<std::string, sdbus::Variant>&, const std::vector<std::string>&)> m_onPropertiesChangedHandler;
std::function<void(const sdbus::ObjectPath&, const std::map<std::string, std::map<std::string, sdbus::Variant>>&)> m_onInterfacesAddedHandler;
std::function<void(const sdbus::ObjectPath&, const std::vector<std::string>&)> m_onInterfacesRemovedHandler; const Message* m_signalMsg{};
std::string m_signalMemberName;
}; };
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTINGPROXY_H_ */ }}
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTPROXY_H_ */

View File

@ -1,246 +0,0 @@
/**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file TestingAdaptor.h
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SDBUS_CPP_INTEGRATIONTESTS_TESTINGADAPTOR_H_
#define SDBUS_CPP_INTEGRATIONTESTS_TESTINGADAPTOR_H_
#include "adaptor-glue.h"
#include <thread>
#include <chrono>
#include <atomic>
class TestingAdaptor : public sdbus::AdaptorInterfaces< testing_adaptor
, sdbus::Properties_adaptor
, sdbus::ObjectManager_adaptor >
{
public:
TestingAdaptor(sdbus::IConnection& connection) :
AdaptorInterfaces(connection, OBJECT_PATH)
{
registerAdaptor();
}
~TestingAdaptor()
{
unregisterAdaptor();
}
protected:
void noArgNoReturn() const override
{
}
int32_t getInt() const override
{
return INT32_VALUE;
}
std::tuple<uint32_t, std::string> getTuple() const override
{
return std::make_tuple(UINT32_VALUE, STRING_VALUE);
}
double multiply(const int64_t& a, const double& b) const override
{
return a * b;
}
void multiplyWithNoReply(const int64_t& a, const double& b) const override
{
m_multiplyResult = a * b;
m_wasMultiplyCalled = true;
}
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x) const override
{
std::vector<int16_t> res{x.get<1>()};
auto y = std::get<std::vector<int16_t>>(x);
res.insert(res.end(), y.begin(), y.end());
return res;
}
sdbus::Variant processVariant(sdbus::Variant& v) override
{
sdbus::Variant res = static_cast<int32_t>(v.get<double>());
return res;
}
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) const override
{
std::map<int32_t, sdbus::Variant> res;
for (auto item : x)
{
res[item] = (item <= 0) ? std::get<0>(y) : std::get<1>(y);
}
return res;
}
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() const override
{
return sdbus::make_struct(STRING_VALUE, sdbus::make_struct(std::map<int32_t, int32_t>{{INT32_VALUE, INT32_VALUE}}));
}
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b) override
{
int32_t res{0};
res += std::get<0>(a) + std::get<1>(a);
res += std::get<0>(b) + std::get<1>(b);
return res;
}
uint32_t sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b) override
{
uint32_t res{0};
for (auto x : a)
{
res += x;
}
for (auto x : b)
{
res += x;
}
return res;
}
uint32_t doOperation(uint32_t param) override
{
std::this_thread::sleep_for(std::chrono::milliseconds(param));
return param;
}
void doOperationAsync(uint32_t param, sdbus::Result<uint32_t> result) override
{
if (param == 0)
{
// Don't sleep and return the result from this thread
result.returnResults(param);
}
else
{
// Process asynchronously in another thread and return the result from there
std::thread([param, result = std::move(result)]()
{
std::this_thread::sleep_for(std::chrono::milliseconds(param));
result.returnResults(param);
}).detach();
}
}
sdbus::Signature getSignature() const override
{
return SIGNATURE_VALUE;
}
sdbus::ObjectPath getObjPath() const override
{
return OBJECT_PATH_VALUE;
}
sdbus::UnixFd getUnixFd() const override
{
return sdbus::UnixFd{UNIX_FD_VALUE};
}
ComplexType getComplex() const override
{
return { // map
{
0, // uint_64_t
{ // struct
{ // map
{
'a', // uint8_t
{ // vector
{ // struct
"/object/path", // object path
false,
3.14,
{ // map
{0, "zero"}
}
}
}
}
},
"a{t(a{ya(obva{is})}gs)}", // signature
""
}
}
};
}
void throwError() const override
{
m_wasThrowErrorCalled = true;
throw sdbus::createError(1, "A test error occurred");
}
void emitTwoSimpleSignals() override
{
emitSimpleSignal();
emitSignalWithMap({});
}
std::string state() override
{
return m_state;
}
uint32_t action() override
{
return m_action;
}
void action(const uint32_t& value) override
{
m_action = value;
}
bool blocking() override
{
return m_blocking;
}
void blocking(const bool& value) override
{
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_wasMultiplyCalled{false};
mutable double m_multiplyResult{};
mutable std::atomic<bool> m_wasThrowErrorCalled{false};
};
#endif /* INTEGRATIONTESTS_TESTINGADAPTOR_H_ */

View File

@ -1,366 +0,0 @@
/**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file adaptor-glue.h
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SDBUS_CPP_INTEGRATIONTESTS_ADAPTOR_GLUE_H_
#define SDBUS_CPP_INTEGRATIONTESTS_ADAPTOR_GLUE_H_
#include "defs.h"
// sdbus
#include "sdbus-c++/sdbus-c++.h"
using ComplexType = std::map<
uint64_t,
sdbus::Struct<
std::map<
uint8_t,
std::vector<
sdbus::Struct<
sdbus::ObjectPath,
bool,
sdbus::Variant,
std::map<int, std::string>
>
>
>,
sdbus::Signature,
std::string // char* leads to type and memory issues, std::string is best choice
>
>;
class testing_adaptor
{
protected:
testing_adaptor(sdbus::IObject& object) :
object_(object)
{
object_.setInterfaceFlags(INTERFACE_NAME).markAsDeprecated().withPropertyUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL);
object_.registerMethod("noArgNoReturn").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->noArgNoReturn(); });
object_.registerMethod("getInt").onInterface(INTERFACE_NAME).withOutputParamNames("anInt").implementedAs([this](){ return this->getInt(); });
object_.registerMethod("getTuple").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getTuple(); });
object_.registerMethod("multiply").onInterface(INTERFACE_NAME).withInputParamNames("a", "b").withOutputParamNames("result").implementedAs([this](const int64_t& a, const double& b){ return this->multiply(a, b); });
object_.registerMethod("multiplyWithNoReply").onInterface(INTERFACE_NAME).implementedAs([this](const int64_t& a, const double& b){ this->multiplyWithNoReply(a, b); }).markAsDeprecated().withNoReply();
object_.registerMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).implementedAs([this](
const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x){ return this->getInts16FromStruct(x); });
object_.registerMethod("processVariant").onInterface(INTERFACE_NAME).implementedAs([this](sdbus::Variant& v){ return this->processVariant(v); });
object_.registerMethod("getMapOfVariants").onInterface(INTERFACE_NAME)
.withInputParamNames("x", "y").withOutputParamNames("aMapOfVariants").implementedAs([this](
const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y){ return this->getMapOfVariants(x ,y); });
object_.registerMethod("getStructInStruct").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getStructInStruct(); });
object_.registerMethod("sumStructItems").onInterface(INTERFACE_NAME).implementedAs([this](
const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b){
return this->sumStructItems(a, b);
});
object_.registerMethod("sumVectorItems").onInterface(INTERFACE_NAME).implementedAs([this](
const std::vector<uint16_t>& a, const std::vector<uint64_t>& b){
return this->sumVectorItems(a, b);
});
object_.registerMethod("doOperation").onInterface(INTERFACE_NAME).implementedAs([this](uint32_t param)
{
return this->doOperation(param);
});
object_.registerMethod("doOperationAsync").onInterface(INTERFACE_NAME).implementedAs([this](sdbus::Result<uint32_t> result, uint32_t param)
{
this->doOperationAsync(param, std::move(result));
});
object_.registerMethod("getSignature").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getSignature(); });
object_.registerMethod("getObjPath").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getObjPath(); });
object_.registerMethod("getUnixFd").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getUnixFd(); });
object_.registerMethod("getComplex").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getComplex(); }).markAsDeprecated();
object_.registerMethod("throwError").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->throwError(); });
object_.registerMethod("throwErrorWithNoReply").onInterface(INTERFACE_NAME).implementedAs([this](){ this->throwError(); }).withNoReply();
object_.registerMethod("doPrivilegedStuff").onInterface(INTERFACE_NAME).implementedAs([](){}).markAsPrivileged();
object_.registerMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME).implementedAs([this](){ this->emitTwoSimpleSignals(); });
// registration of signals is optional, it is useful because of introspection
object_.registerSignal("simpleSignal").onInterface(INTERFACE_NAME).markAsDeprecated();
// Note: sd-bus of libsystemd up to (including) v244 has a bug where it doesn't generate signal parameter names in introspection XML. Signal param names commented temporarily.
object_.registerSignal("signalWithMap").onInterface(INTERFACE_NAME).withParameters<std::map<int32_t, std::string>>(/*"aMap"*/);
object_.registerSignal("signalWithVariant").onInterface(INTERFACE_NAME).withParameters<sdbus::Variant>(/*"aVariant"*/);
object_.registerProperty("state").onInterface(INTERFACE_NAME).withGetter([this](){ return this->state(); }).markAsDeprecated().withUpdateBehavior(sdbus::Flags::CONST_PROPERTY_VALUE);
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 emitSimpleSignal()
{
object_.emitSignal("simpleSignal").onInterface(INTERFACE_NAME);
}
void emitSignalWithMap(const std::map<int32_t, std::string>& map)
{
object_.emitSignal("signalWithMap").onInterface(INTERFACE_NAME).withArguments(map);
}
void emitSignalWithVariant(const sdbus::Variant& v)
{
object_.emitSignal("signalWithVariant").onInterface(INTERFACE_NAME).withArguments(v);
}
void emitSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
{
object_.emitSignal("signalWithoutRegistration").onInterface(INTERFACE_NAME).withArguments(s);
}
void emitSignalOnNonexistentInterface()
{
object_.emitSignal("simpleSignal").onInterface("sdbuscpp.interface.that.does.not.exist");
}
private:
sdbus::IObject& object_;
protected:
virtual void noArgNoReturn() const = 0;
virtual int32_t getInt() const = 0;
virtual std::tuple<uint32_t, std::string> getTuple() const = 0;
virtual double multiply(const int64_t& a, const double& b) const = 0;
virtual void multiplyWithNoReply(const int64_t& a, const double& b) const = 0;
virtual std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x) const = 0;
virtual sdbus::Variant processVariant(sdbus::Variant& v) = 0;
virtual std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) const = 0;
virtual sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() const = 0;
virtual int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b) = 0;
virtual uint32_t sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b) = 0;
virtual uint32_t doOperation(uint32_t param) = 0;
virtual void doOperationAsync(uint32_t param, sdbus::Result<uint32_t> result) = 0;
virtual sdbus::Signature getSignature() const = 0;
virtual sdbus::ObjectPath getObjPath() const = 0;
virtual sdbus::UnixFd getUnixFd() const = 0;
virtual ComplexType getComplex() const = 0;
virtual void throwError() const = 0;
virtual void emitTwoSimpleSignals() = 0;
virtual std::string state() = 0;
virtual uint32_t action() = 0;
virtual void action(const uint32_t& value) = 0;
virtual bool blocking() = 0;
virtual void blocking(const bool& value) = 0;
public: // For testing purposes
std::string getExpectedXmlApiDescription()
{
return
R"delimiter(<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.DBus.Peer">
<method name="Ping"/>
<method name="GetMachineId">
<arg type="s" name="machine_uuid" direction="out"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" type="s" direction="out"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg name="interface" direction="in" type="s"/>
<arg name="property" direction="in" type="s"/>
<arg name="value" direction="out" type="v"/>
</method>
<method name="GetAll">
<arg name="interface" direction="in" type="s"/>
<arg name="properties" direction="out" type="a{sv}"/>
</method>
<method name="Set">
<arg name="interface" direction="in" type="s"/>
<arg name="property" direction="in" type="s"/>
<arg name="value" direction="in" type="v"/>
</method>
<signal name="PropertiesChanged">
<arg type="s" name="interface"/>
<arg type="a{sv}" name="changed_properties"/>
<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">
<arg type="u" direction="in"/>
<arg type="u" direction="out"/>
</method>
<method name="doOperationAsync">
<arg type="u" direction="in"/>
<arg type="u" direction="out"/>
</method>
<method name="doPrivilegedStuff">
<annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
</method>
<method name="emitTwoSimpleSignals">
</method>
<method name="getComplex">
<arg type="a{t(a{ya(obva{is})}gs)}" direction="out"/>
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
</method>
<method name="getInt">)delimiter"
#if LIBSYSTEMD_VERSION>=242
R"delimiter(
<arg type="i" name="anInt" direction="out"/>)delimiter"
#else
R"delimiter(
<arg type="i" direction="out"/>)delimiter"
#endif
R"delimiter(
</method>
<method name="getInts16FromStruct">
<arg type="(yndsan)" direction="in"/>
<arg type="an" direction="out"/>
</method>
<method name="getMapOfVariants">)delimiter"
#if LIBSYSTEMD_VERSION>=242
R"delimiter(
<arg type="ai" name="x" direction="in"/>
<arg type="(vv)" name="y" direction="in"/>
<arg type="a{iv}" name="aMapOfVariants" direction="out"/>)delimiter"
#else
R"delimiter(
<arg type="ai" direction="in"/>
<arg type="(vv)" direction="in"/>
<arg type="a{iv}" direction="out"/>)delimiter"
#endif
R"delimiter(
</method>
<method name="getObjPath">
<arg type="o" direction="out"/>
</method>
<method name="getSignature">
<arg type="g" direction="out"/>
</method>
<method name="getStructInStruct">
<arg type="(s(a{ii}))" direction="out"/>
</method>
<method name="getTuple">
<arg type="u" direction="out"/>
<arg type="s" direction="out"/>
</method>
<method name="getUnixFd">
<arg type="h" direction="out"/>
</method>
<method name="multiply">)delimiter"
#if LIBSYSTEMD_VERSION>=242
R"delimiter(
<arg type="x" name="a" direction="in"/>
<arg type="d" name="b" direction="in"/>
<arg type="d" name="result" direction="out"/>)delimiter"
#else
R"delimiter(
<arg type="x" direction="in"/>
<arg type="d" direction="in"/>
<arg type="d" direction="out"/>)delimiter"
#endif
R"delimiter(
</method>
<method name="multiplyWithNoReply">
<arg type="x" direction="in"/>
<arg type="d" direction="in"/>
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="noArgNoReturn">
</method>
<method name="processVariant">
<arg type="v" direction="in"/>
<arg type="v" direction="out"/>
</method>
<method name="sumStructItems">
<arg type="(yq)" direction="in"/>
<arg type="(ix)" direction="in"/>
<arg type="i" direction="out"/>
</method>
<method name="sumVectorItems">
<arg type="aq" direction="in"/>
<arg type="at" direction="in"/>
<arg type="u" direction="out"/>
</method>
<method name="throwError">
</method>
<method name="throwErrorWithNoReply">
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<signal name="signalWithMap">
<arg type="a{is}"/>
</signal>
<signal name="signalWithVariant">
<arg type="v"/>
</signal>
<signal name="simpleSignal">
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
</signal>
<property name="action" type="u" access="readwrite">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="invalidates"/>
</property>
<property name="blocking" type="b" access="readwrite">
</property>
<property name="state" type="s" access="read">
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
</property>
</interface>
</node>
)delimiter";
}
};
#endif /* SDBUS_CPP_INTEGRATIONTESTS_ADAPTOR_GLUE_H_ */

View File

@ -11,6 +11,9 @@
<allow own="org.sdbuscpp.integrationtests"/> <allow own="org.sdbuscpp.integrationtests"/>
<allow send_destination="org.sdbuscpp.integrationtests"/> <allow send_destination="org.sdbuscpp.integrationtests"/>
<allow send_interface="org.sdbuscpp.integrationtests"/> <allow send_interface="org.sdbuscpp.integrationtests"/>
<allow own="org.sdbuscpp.integrationtests2"/>
<allow send_destination="org.sdbuscpp.integrationtests2"/>
</policy> </policy>
</busconfig> </busconfig>

View File

@ -0,0 +1,109 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__integrationtests_adaptor_h__adaptor__H__
#define __sdbuscpp__integrationtests_adaptor_h__adaptor__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace sdbuscpp {
class integrationtests_adaptor
{
public:
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.integrationtests";
protected:
integrationtests_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.setInterfaceFlags(INTERFACE_NAME).markAsDeprecated().withPropertyUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL);
object_.registerMethod("noArgNoReturn").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->noArgNoReturn(); });
object_.registerMethod("getInt").onInterface(INTERFACE_NAME).withOutputParamNames("anInt").implementedAs([this](){ return this->getInt(); });
object_.registerMethod("getTuple").onInterface(INTERFACE_NAME).withOutputParamNames("arg0", "arg1").implementedAs([this](){ return this->getTuple(); });
object_.registerMethod("multiply").onInterface(INTERFACE_NAME).withInputParamNames("a", "b").withOutputParamNames("result").implementedAs([this](const int64_t& a, const double& b){ return this->multiply(a, b); });
object_.registerMethod("multiplyWithNoReply").onInterface(INTERFACE_NAME).withInputParamNames("a", "b").implementedAs([this](const int64_t& a, const double& b){ return this->multiplyWithNoReply(a, b); }).markAsDeprecated().withNoReply();
object_.registerMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0){ return this->getInts16FromStruct(arg0); });
object_.registerMethod("processVariant").onInterface(INTERFACE_NAME).withInputParamNames("variant").withOutputParamNames("result").implementedAs([this](const sdbus::Variant& variant){ return this->processVariant(variant); });
object_.registerMethod("getMapOfVariants").onInterface(INTERFACE_NAME).withInputParamNames("x", "y").withOutputParamNames("aMapOfVariants").implementedAs([this](const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y){ return this->getMapOfVariants(x, y); });
object_.registerMethod("getStructInStruct").onInterface(INTERFACE_NAME).withOutputParamNames("aMapOfVariants").implementedAs([this](){ return this->getStructInStruct(); });
object_.registerMethod("sumStructItems").onInterface(INTERFACE_NAME).withInputParamNames("arg0", "arg1").withOutputParamNames("arg0").implementedAs([this](const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1){ return this->sumStructItems(arg0, arg1); });
object_.registerMethod("sumVectorItems").onInterface(INTERFACE_NAME).withInputParamNames("arg0", "arg1").withOutputParamNames("arg0").implementedAs([this](const std::vector<uint16_t>& arg0, const std::vector<uint64_t>& arg1){ return this->sumVectorItems(arg0, arg1); });
object_.registerMethod("doOperation").onInterface(INTERFACE_NAME).withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](const uint32_t& arg0){ return this->doOperation(arg0); });
object_.registerMethod("doOperationAsync").onInterface(INTERFACE_NAME).withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](sdbus::Result<uint32_t>&& result, uint32_t arg0){ this->doOperationAsync(std::move(result), std::move(arg0)); });
object_.registerMethod("getSignature").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getSignature(); });
object_.registerMethod("getObjPath").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getObjPath(); });
object_.registerMethod("getUnixFd").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getUnixFd(); });
object_.registerMethod("getComplex").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getComplex(); }).markAsDeprecated();
object_.registerMethod("throwError").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->throwError(); });
object_.registerMethod("throwErrorWithNoReply").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->throwErrorWithNoReply(); }).withNoReply();
object_.registerMethod("doPrivilegedStuff").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->doPrivilegedStuff(); }).markAsPrivileged();
object_.registerMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->emitTwoSimpleSignals(); });
object_.registerSignal("simpleSignal").onInterface(INTERFACE_NAME).markAsDeprecated();
object_.registerSignal("signalWithMap").onInterface(INTERFACE_NAME).withParameters<std::map<int32_t, std::string>>("aMap");
object_.registerSignal("signalWithVariant").onInterface(INTERFACE_NAME).withParameters<sdbus::Variant>("aVariant");
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("state").onInterface(INTERFACE_NAME).withGetter([this](){ return this->state(); }).markAsDeprecated().withUpdateBehavior(sdbus::Flags::CONST_PROPERTY_VALUE);
}
~integrationtests_adaptor() = default;
public:
void emitSimpleSignal()
{
object_.emitSignal("simpleSignal").onInterface(INTERFACE_NAME);
}
void emitSignalWithMap(const std::map<int32_t, std::string>& aMap)
{
object_.emitSignal("signalWithMap").onInterface(INTERFACE_NAME).withArguments(aMap);
}
void emitSignalWithVariant(const sdbus::Variant& aVariant)
{
object_.emitSignal("signalWithVariant").onInterface(INTERFACE_NAME).withArguments(aVariant);
}
private:
virtual void noArgNoReturn() = 0;
virtual int32_t getInt() = 0;
virtual std::tuple<uint32_t, std::string> getTuple() = 0;
virtual double multiply(const int64_t& a, const double& b) = 0;
virtual void multiplyWithNoReply(const int64_t& a, const double& b) = 0;
virtual std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0) = 0;
virtual sdbus::Variant processVariant(const sdbus::Variant& variant) = 0;
virtual std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) = 0;
virtual sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() = 0;
virtual int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1) = 0;
virtual uint32_t sumVectorItems(const std::vector<uint16_t>& arg0, const std::vector<uint64_t>& arg1) = 0;
virtual uint32_t doOperation(const uint32_t& arg0) = 0;
virtual void doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t arg0) = 0;
virtual sdbus::Signature getSignature() = 0;
virtual sdbus::ObjectPath getObjPath() = 0;
virtual sdbus::UnixFd getUnixFd() = 0;
virtual std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() = 0;
virtual void throwError() = 0;
virtual void throwErrorWithNoReply() = 0;
virtual void doPrivilegedStuff() = 0;
virtual void emitTwoSimpleSignals() = 0;
private:
virtual uint32_t action() = 0;
virtual void action(const uint32_t& value) = 0;
virtual bool blocking() = 0;
virtual void blocking(const bool& value) = 0;
virtual std::string state() = 0;
private:
sdbus::IObject& object_;
};
}} // namespaces
#endif

View File

@ -0,0 +1,215 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__integrationtests_proxy_h__proxy__H__
#define __sdbuscpp__integrationtests_proxy_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace sdbuscpp {
class integrationtests_proxy
{
public:
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.integrationtests";
protected:
integrationtests_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
proxy_.uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); });
proxy_.uponSignal("signalWithMap").onInterface(INTERFACE_NAME).call([this](const std::map<int32_t, std::string>& aMap){ this->onSignalWithMap(aMap); });
proxy_.uponSignal("signalWithVariant").onInterface(INTERFACE_NAME).call([this](const sdbus::Variant& aVariant){ this->onSignalWithVariant(aVariant); });
}
~integrationtests_proxy() = default;
virtual void onSimpleSignal() = 0;
virtual void onSignalWithMap(const std::map<int32_t, std::string>& aMap) = 0;
virtual void onSignalWithVariant(const sdbus::Variant& aVariant) = 0;
public:
void noArgNoReturn()
{
proxy_.callMethod("noArgNoReturn").onInterface(INTERFACE_NAME);
}
int32_t getInt()
{
int32_t result;
proxy_.callMethod("getInt").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
std::tuple<uint32_t, std::string> getTuple()
{
std::tuple<uint32_t, std::string> result;
proxy_.callMethod("getTuple").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
double multiply(const int64_t& a, const double& b)
{
double result;
proxy_.callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
return result;
}
void multiplyWithNoReply(const int64_t& a, const double& b)
{
proxy_.callMethod("multiplyWithNoReply").onInterface(INTERFACE_NAME).withArguments(a, b).dontExpectReply();
}
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0)
{
std::vector<int16_t> result;
proxy_.callMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
return result;
}
sdbus::Variant processVariant(const sdbus::Variant& variant)
{
sdbus::Variant result;
proxy_.callMethod("processVariant").onInterface(INTERFACE_NAME).withArguments(variant).storeResultsTo(result);
return result;
}
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y)
{
std::map<int32_t, sdbus::Variant> result;
proxy_.callMethod("getMapOfVariants").onInterface(INTERFACE_NAME).withArguments(x, y).storeResultsTo(result);
return result;
}
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct()
{
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> result;
proxy_.callMethod("getStructInStruct").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1)
{
int32_t result;
proxy_.callMethod("sumStructItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result);
return result;
}
uint32_t sumVectorItems(const std::vector<uint16_t>& arg0, const std::vector<uint64_t>& arg1)
{
uint32_t result;
proxy_.callMethod("sumVectorItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result);
return result;
}
uint32_t doOperation(const uint32_t& arg0)
{
uint32_t result;
proxy_.callMethod("doOperation").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
return result;
}
uint32_t doOperationAsync(const uint32_t& arg0)
{
uint32_t result;
proxy_.callMethod("doOperationAsync").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
return result;
}
sdbus::Signature getSignature()
{
sdbus::Signature result;
proxy_.callMethod("getSignature").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
sdbus::ObjectPath getObjPath()
{
sdbus::ObjectPath result;
proxy_.callMethod("getObjPath").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
sdbus::UnixFd getUnixFd()
{
sdbus::UnixFd result;
proxy_.callMethod("getUnixFd").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex()
{
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> result;
proxy_.callMethod("getComplex").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
void throwError()
{
proxy_.callMethod("throwError").onInterface(INTERFACE_NAME);
}
void throwErrorWithNoReply()
{
proxy_.callMethod("throwErrorWithNoReply").onInterface(INTERFACE_NAME).dontExpectReply();
}
void doPrivilegedStuff()
{
proxy_.callMethod("doPrivilegedStuff").onInterface(INTERFACE_NAME);
}
void emitTwoSimpleSignals()
{
proxy_.callMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME);
}
void unregisterSimpleSignalHandler()
{
proxy_.muteSignal("simpleSignal").onInterface(INTERFACE_NAME);
}
void reRegisterSimpleSignalHandler()
{
proxy_.uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); });
proxy_.finishRegistration();
}
public:
uint32_t action()
{
return proxy_.getProperty("action").onInterface(INTERFACE_NAME);
}
void action(const uint32_t& value)
{
proxy_.setProperty("action").onInterface(INTERFACE_NAME).toValue(value);
}
bool blocking()
{
return proxy_.getProperty("blocking").onInterface(INTERFACE_NAME);
}
void blocking(const bool& value)
{
proxy_.setProperty("blocking").onInterface(INTERFACE_NAME).toValue(value);
}
std::string state()
{
return proxy_.getProperty("state").onInterface(INTERFACE_NAME);
}
private:
sdbus::IProxy& proxy_;
};
}} // namespaces
#endif

View File

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<node name="/org/sdbuscpp/integrationtest">
<interface name="org.sdbuscpp.integrationtests">
<annotation name="org.freedesktop.DBus.Deprecated" value="true" />
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false" />
<method name="noArgNoReturn">
</method>
<method name="getInt">
<arg type="i" name="anInt" direction="out" />
</method>
<method name="getTuple">
<arg type="u" direction="out" />
<arg type="s" direction="out" />
</method>
<method name="multiply">
<arg type="x" name="a" direction="in" />
<arg type="d" name="b" direction="in" />
<arg type="d" name="result" direction="out" />
</method>
<method name="multiplyWithNoReply">
<annotation name="org.freedesktop.DBus.Deprecated" value="true" />
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true" />
<arg type="x" name="a" direction="in" />
<arg type="d" name="b" direction="in" />
</method>
<method name="getInts16FromStruct">
<arg type="(yndsan)" direction="in" />
<arg type="an" direction="out" />
</method>
<method name="processVariant">
<arg type="v" name="variant" direction="in" />
<arg type="v" name="result" direction="out" />
</method>
<method name="getMapOfVariants">
<arg type="ai" name="x" direction="in" />
<arg type="(vv)" name="y" direction="in" />
<arg type="a{iv}" name="aMapOfVariants" direction="out" />
</method>
<method name="getStructInStruct">
<arg type="(s(a{ii}))" name="aMapOfVariants" direction="out" />
</method>
<method name="sumStructItems">
<arg type="(yq)" direction="in" />
<arg type="(ix)" direction="in" />
<arg type="i" direction="out" />
</method>
<method name="sumVectorItems">
<arg type="aq" direction="in" />
<arg type="at" direction="in" />
<arg type="u" direction="out" />
</method>
<method name="doOperation">
<arg type="u" direction="in" />
<arg type="u" direction="out" />
</method>
<method name="doOperationAsync">
<annotation name="org.freedesktop.DBus.Method.Async" value="server" />
<arg type="u" direction="in" />
<arg type="u" direction="out" />
</method>
<method name="getSignature">
<arg type="g" direction="out" />
</method>
<method name="getObjPath">
<arg type="o" direction="out" />
</method>
<method name="getUnixFd">
<arg type="h" direction="out" />
</method>
<method name="getComplex">
<annotation name="org.freedesktop.DBus.Deprecated" value="true" />
<arg type="a{t(a{ya(obva{is})}gs)}" direction="out" />
</method>
<method name="throwError">
</method>
<method name="throwErrorWithNoReply">
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true" />
</method>
<method name="doPrivilegedStuff">
<annotation name="org.freedesktop.systemd1.Privileged" value="true" />
</method>
<method name="emitTwoSimpleSignals">
</method>
<signal name="simpleSignal">
<annotation name="org.freedesktop.DBus.Deprecated" value="true" />
</signal>
<signal name="signalWithMap">
<arg type="a{is}" name="aMap" />
</signal>
<signal name="signalWithVariant">
<arg type="v" name="aVariant" />
</signal>
<property name="action" type="u" access="readwrite">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="invalidates"/>
</property>
<property name="blocking" type="b" access="readwrite">
</property>
<property name="state" type="s" access="read">
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
</property>
</interface>
</node>

View File

@ -1,282 +0,0 @@
/**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file proxy-glue.h
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SDBUS_CPP_INTEGRATIONTESTS_PROXY_GLUE_H_
#define SDBUS_CPP_INTEGRATIONTESTS_PROXY_GLUE_H_
#include "defs.h"
// sdbus
#include "sdbus-c++/sdbus-c++.h"
class testing_proxy
{
protected:
testing_proxy(sdbus::IProxy& object) :
object_(object)
{
object_.uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); });
object_.uponSignal("signalWithMap").onInterface(INTERFACE_NAME).call([this](const std::map<int32_t, std::string>& map){ this->onSignalWithMap(map); });
object_.uponSignal("signalWithVariant").onInterface(INTERFACE_NAME).call([this](const sdbus::Variant& v){ this->onSignalWithVariant(v); });
object_.uponSignal("signalWithoutRegistration").onInterface(INTERFACE_NAME).call([this](const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
{ 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;
virtual void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s) = 0;
virtual void onDoOperationReply(uint32_t returnValue, const sdbus::Error* error) = 0;
public:
void emitTwoSimpleSignals()
{
object_.callMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME);
}
void noArgNoReturn()
{
object_.callMethod("noArgNoReturn").onInterface(INTERFACE_NAME);
}
int32_t getInt()
{
int32_t result;
object_.callMethod("getInt").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
std::tuple<uint32_t, std::string> getTuple()
{
std::tuple<uint32_t, std::string> result;
object_.callMethod("getTuple").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
double multiply(const int64_t& a, const double& b)
{
double result;
object_.callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
return result;
}
void multiplyWithNoReply(const int64_t& a, const double& b)
{
object_.callMethod("multiplyWithNoReply").onInterface(INTERFACE_NAME).withArguments(a, b).dontExpectReply();
}
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x)
{
std::vector<int16_t> result;
object_.callMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).withArguments(x).storeResultsTo(result);
return result;
}
sdbus::Variant processVariant(const sdbus::Variant& v)
{
sdbus::Variant result;
object_.callMethod("processVariant").onInterface(INTERFACE_NAME).withArguments(v).storeResultsTo(result);
return result;
}
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y)
{
std::map<int32_t, sdbus::Variant> result;
object_.callMethod("getMapOfVariants").onInterface(INTERFACE_NAME).withArguments(x, y).storeResultsTo(result);
return result;
}
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct()
{
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> result;
object_.callMethod("getStructInStruct").onInterface(INTERFACE_NAME).withArguments().storeResultsTo(result);
return result;
}
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b)
{
int32_t result;
object_.callMethod("sumStructItems").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
return result;
}
uint32_t sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b)
{
uint32_t result;
object_.callMethod("sumVectorItems").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
return result;
}
uint32_t doOperation(uint32_t param)
{
uint32_t result;
object_.callMethod("doOperation").onInterface(INTERFACE_NAME).withArguments(param).storeResultsTo(result);
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;
object_.callMethod("doOperationAsync").onInterface(INTERFACE_NAME).withArguments(param).storeResultsTo(result);
return result;
}
sdbus::PendingAsyncCall doOperationClientSideAsync(uint32_t param)
{
return object_.callMethodAsync("doOperation")
.onInterface(INTERFACE_NAME)
.withArguments(param)
.uponReplyInvoke([this](const sdbus::Error* error, uint32_t returnValue)
{
this->onDoOperationReply(returnValue, error);
});
}
void doErroneousOperationClientSideAsync()
{
object_.callMethodAsync("throwError")
.onInterface(INTERFACE_NAME)
.uponReplyInvoke([this](const sdbus::Error* error)
{
this->onDoOperationReply(0, error);
});
}
void doOperationClientSideAsyncWith500msTimeout(uint32_t param)
{
using namespace std::chrono_literals;
object_.callMethodAsync("doOperation")
.onInterface(INTERFACE_NAME)
.withTimeout(500000us)
.withArguments(param)
.uponReplyInvoke([this](const sdbus::Error* error, uint32_t returnValue)
{
this->onDoOperationReply(returnValue, error);
});
}
sdbus::Signature getSignature()
{
sdbus::Signature result;
object_.callMethod("getSignature").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
sdbus::ObjectPath getObjPath()
{
sdbus::ObjectPath result;
object_.callMethod("getObjPath").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
sdbus::UnixFd getUnixFd()
{
sdbus::UnixFd result;
object_.callMethod("getUnixFd").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
ComplexType getComplex()
{
ComplexType result;
object_.callMethod("getComplex").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
void throwError()
{
object_.callMethod("throwError").onInterface(INTERFACE_NAME);
}
void throwErrorWithNoReply()
{
object_.callMethod("throwErrorWithNoReply").onInterface(INTERFACE_NAME).dontExpectReply();
}
int32_t callNonexistentMethod()
{
int32_t result;
object_.callMethod("callNonexistentMethod").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
int32_t callMethodOnNonexistentInterface()
{
int32_t result;
object_.callMethod("someMethod").onInterface("sdbuscpp.interface.that.does.not.exist").storeResultsTo(result);
return result;
}
std::string state()
{
return object_.getProperty("state").onInterface(INTERFACE_NAME);
}
void state(const std::string& value)
{
object_.setProperty("state").onInterface(INTERFACE_NAME).toValue(value);
}
uint32_t action()
{
return object_.getProperty("action").onInterface(INTERFACE_NAME);
}
void action(const uint32_t& value)
{
object_.setProperty("action").onInterface(INTERFACE_NAME).toValue(value);
}
bool blocking()
{
return object_.getProperty("blocking").onInterface(INTERFACE_NAME);
}
void blocking(const bool& value)
{
object_.setProperty("blocking").onInterface(INTERFACE_NAME).toValue(value);
}
private:
sdbus::IProxy& object_;
};
#endif /* SDBUS_CPP_INTEGRATIONTESTS_PROXY_GLUE_H_ */

View File

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

View File

@ -39,7 +39,7 @@ using namespace std::chrono_literals;
uint64_t totalDuration = 0; uint64_t totalDuration = 0;
class PerftestProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::perftests_proxy> class PerftestProxy final : public sdbus::ProxyInterfaces<org::sdbuscpp::perftests_proxy>
{ {
public: public:
PerftestProxy(std::string destination, std::string objectPath) PerftestProxy(std::string destination, std::string objectPath)
@ -54,7 +54,7 @@ public:
} }
protected: protected:
virtual void onDataSignal(const std::string& data) override virtual void onDataSignal([[maybe_unused]] const std::string& data) override
{ {
static unsigned int counter = 0; static unsigned int counter = 0;
static std::chrono::time_point<std::chrono::steady_clock> startTime; static std::chrono::time_point<std::chrono::steady_clock> startTime;

View File

@ -36,7 +36,7 @@ using namespace std::chrono_literals;
std::string createRandomString(size_t length); std::string createRandomString(size_t length);
class PerftestAdaptor : public sdbus::AdaptorInterfaces<org::sdbuscpp::perftests_adaptor> class PerftestAdaptor final : public sdbus::AdaptorInterfaces<org::sdbuscpp::perftests_adaptor>
{ {
public: public:
PerftestAdaptor(sdbus::IConnection& connection, std::string objectPath) PerftestAdaptor(sdbus::IConnection& connection, std::string objectPath)

View File

@ -53,7 +53,7 @@ using namespace std::string_literals;
#define FAHRENHEIT_THERMOMETER_OBJECT_PATH "/org/sdbuscpp/stresstests/fahrenheit/thermometer"s #define FAHRENHEIT_THERMOMETER_OBJECT_PATH "/org/sdbuscpp/stresstests/fahrenheit/thermometer"s
#define CONCATENATOR_OBJECT_PATH "/org/sdbuscpp/stresstests/concatenator"s #define CONCATENATOR_OBJECT_PATH "/org/sdbuscpp/stresstests/concatenator"s
class CelsiusThermometerAdaptor : public sdbus::AdaptorInterfaces<org::sdbuscpp::stresstests::celsius::thermometer_adaptor> class CelsiusThermometerAdaptor final : public sdbus::AdaptorInterfaces<org::sdbuscpp::stresstests::celsius::thermometer_adaptor>
{ {
public: public:
CelsiusThermometerAdaptor(sdbus::IConnection& connection, std::string objectPath) CelsiusThermometerAdaptor(sdbus::IConnection& connection, std::string objectPath)
@ -92,8 +92,8 @@ public:
} }
}; };
class FahrenheitThermometerAdaptor : public sdbus::AdaptorInterfaces< org::sdbuscpp::stresstests::fahrenheit::thermometer_adaptor class FahrenheitThermometerAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::stresstests::fahrenheit::thermometer_adaptor
, org::sdbuscpp::stresstests::fahrenheit::thermometer::factory_adaptor > , org::sdbuscpp::stresstests::fahrenheit::thermometer::factory_adaptor >
{ {
public: public:
FahrenheitThermometerAdaptor(sdbus::IConnection& connection, std::string objectPath, bool isDelegate) FahrenheitThermometerAdaptor(sdbus::IConnection& connection, std::string objectPath, bool isDelegate)
@ -222,7 +222,7 @@ public:
} }
}; };
class ConcatenatorAdaptor : public sdbus::AdaptorInterfaces<org::sdbuscpp::stresstests::concatenator_adaptor> class ConcatenatorAdaptor final : public sdbus::AdaptorInterfaces<org::sdbuscpp::stresstests::concatenator_adaptor>
{ {
public: public:
ConcatenatorAdaptor(sdbus::IConnection& connection, std::string objectPath) ConcatenatorAdaptor(sdbus::IConnection& connection, std::string objectPath)
@ -294,7 +294,7 @@ private:
std::atomic<bool> exit_{}; std::atomic<bool> exit_{};
}; };
class ConcatenatorProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstests::concatenator_proxy> class ConcatenatorProxy final : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstests::concatenator_proxy>
{ {
public: public:
ConcatenatorProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath) ConcatenatorProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
@ -309,7 +309,7 @@ public:
} }
private: private:
virtual void onConcatenateReply(const std::string& result, const sdbus::Error* error) override virtual void onConcatenateReply(const std::string& result, [[maybe_unused]] const sdbus::Error* error) override
{ {
assert(error == nullptr); assert(error == nullptr);
@ -457,7 +457,7 @@ int main(int argc, char *argv[])
FahrenheitThermometerProxy thermometer(con, SERVICE_1_BUS_NAME, FAHRENHEIT_THERMOMETER_OBJECT_PATH); FahrenheitThermometerProxy thermometer(con, SERVICE_1_BUS_NAME, FAHRENHEIT_THERMOMETER_OBJECT_PATH);
uint32_t localCounter{}; uint32_t localCounter{};
uint32_t previousTemperature{}; [[maybe_unused]] uint32_t previousTemperature{};
while (!stopClients) while (!stopClients)
{ {

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file Connection_test.cpp * @file Connection_test.cpp
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru) * @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@ -35,10 +35,7 @@ using ::testing::DoAll;
using ::testing::SetArgPointee; using ::testing::SetArgPointee;
using ::testing::Return; using ::testing::Return;
using ::testing::NiceMock; using ::testing::NiceMock;
using sdbus::internal::Connection; 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 class ConnectionCreationTest : public ::testing::Test
{ {
@ -49,61 +46,89 @@ protected:
sd_bus* fakeBusPtr_ = reinterpret_cast<sd_bus*>(1); sd_bus* fakeBusPtr_ = reinterpret_cast<sd_bus*>(1);
}; };
using ADefaultBusConnection = ConnectionCreationTest;
using ASystemBusConnection = ConnectionCreationTest; using ASystemBusConnection = ConnectionCreationTest;
using ASessionBusConnection = ConnectionCreationTest; using ASessionBusConnection = ConnectionCreationTest;
TEST_F(ADefaultBusConnection, OpensAndFlushesBusWhenCreated)
{
EXPECT_CALL(*sdBusIntfMock_, sd_bus_open(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
EXPECT_CALL(*sdBusIntfMock_, sd_bus_flush(_)).Times(1);
Connection(std::move(sdBusIntfMock_), Connection::default_bus);
}
TEST_F(ASystemBusConnection, OpensAndFlushesBusWhenCreated) TEST_F(ASystemBusConnection, OpensAndFlushesBusWhenCreated)
{ {
EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_system(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1))); EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_system(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
EXPECT_CALL(*sdBusIntfMock_, sd_bus_flush(_)).Times(1); EXPECT_CALL(*sdBusIntfMock_, sd_bus_flush(_)).Times(1);
Connection(std::move(sdBusIntfMock_), system_bus); Connection(std::move(sdBusIntfMock_), Connection::system_bus);
} }
TEST_F(ASessionBusConnection, OpensAndFlushesBusWhenCreated) TEST_F(ASessionBusConnection, OpensAndFlushesBusWhenCreated)
{ {
EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1))); EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
EXPECT_CALL(*sdBusIntfMock_, sd_bus_flush(_)).Times(1); EXPECT_CALL(*sdBusIntfMock_, sd_bus_flush(_)).Times(1);
Connection(std::move(sdBusIntfMock_), session_bus); Connection(std::move(sdBusIntfMock_), Connection::session_bus);
}
TEST_F(ADefaultBusConnection, ClosesAndUnrefsBusWhenDestructed)
{
ON_CALL(*sdBusIntfMock_, sd_bus_open(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
EXPECT_CALL(*sdBusIntfMock_, sd_bus_flush_close_unref(_)).Times(1);
Connection(std::move(sdBusIntfMock_), Connection::default_bus);
} }
TEST_F(ASystemBusConnection, ClosesAndUnrefsBusWhenDestructed) TEST_F(ASystemBusConnection, ClosesAndUnrefsBusWhenDestructed)
{ {
ON_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1))); ON_CALL(*sdBusIntfMock_, sd_bus_open_system(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
EXPECT_CALL(*sdBusIntfMock_, sd_bus_flush_close_unref(_)).Times(1); EXPECT_CALL(*sdBusIntfMock_, sd_bus_flush_close_unref(_)).Times(1);
Connection(std::move(sdBusIntfMock_), session_bus); Connection(std::move(sdBusIntfMock_), Connection::system_bus);
} }
TEST_F(ASessionBusConnection, ClosesAndUnrefsBusWhenDestructed) TEST_F(ASessionBusConnection, ClosesAndUnrefsBusWhenDestructed)
{ {
ON_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1))); ON_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
EXPECT_CALL(*sdBusIntfMock_, sd_bus_flush_close_unref(_)).Times(1); EXPECT_CALL(*sdBusIntfMock_, sd_bus_flush_close_unref(_)).Times(1);
Connection(std::move(sdBusIntfMock_), session_bus); Connection(std::move(sdBusIntfMock_), Connection::session_bus);
}
TEST_F(ADefaultBusConnection, ThrowsErrorWhenOpeningTheBusFailsDuringConstruction)
{
ON_CALL(*sdBusIntfMock_, sd_bus_open(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(-1)));
ASSERT_THROW(Connection(std::move(sdBusIntfMock_), Connection::default_bus), sdbus::Error);
} }
TEST_F(ASystemBusConnection, ThrowsErrorWhenOpeningTheBusFailsDuringConstruction) TEST_F(ASystemBusConnection, ThrowsErrorWhenOpeningTheBusFailsDuringConstruction)
{ {
ON_CALL(*sdBusIntfMock_, sd_bus_open_system(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(-1))); ON_CALL(*sdBusIntfMock_, sd_bus_open_system(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(-1)));
ASSERT_THROW(Connection(std::move(sdBusIntfMock_), system_bus), sdbus::Error); ASSERT_THROW(Connection(std::move(sdBusIntfMock_), Connection::system_bus), sdbus::Error);
} }
TEST_F(ASessionBusConnection, ThrowsErrorWhenOpeningTheBusFailsDuringConstruction) TEST_F(ASessionBusConnection, ThrowsErrorWhenOpeningTheBusFailsDuringConstruction)
{ {
ON_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(-1))); ON_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(-1)));
ASSERT_THROW(Connection(std::move(sdBusIntfMock_), session_bus), sdbus::Error); ASSERT_THROW(Connection(std::move(sdBusIntfMock_), Connection::session_bus), sdbus::Error);
}
TEST_F(ADefaultBusConnection, ThrowsErrorWhenFlushingTheBusFailsDuringConstruction)
{
ON_CALL(*sdBusIntfMock_, sd_bus_open(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
ON_CALL(*sdBusIntfMock_, sd_bus_flush(_)).WillByDefault(Return(-1));
ASSERT_THROW(Connection(std::move(sdBusIntfMock_), Connection::default_bus), sdbus::Error);
} }
TEST_F(ASystemBusConnection, ThrowsErrorWhenFlushingTheBusFailsDuringConstruction) TEST_F(ASystemBusConnection, ThrowsErrorWhenFlushingTheBusFailsDuringConstruction)
{ {
ON_CALL(*sdBusIntfMock_, sd_bus_open_system(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1))); ON_CALL(*sdBusIntfMock_, sd_bus_open_system(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
ON_CALL(*sdBusIntfMock_, sd_bus_flush(_)).WillByDefault(Return(-1)); ON_CALL(*sdBusIntfMock_, sd_bus_flush(_)).WillByDefault(Return(-1));
ASSERT_THROW(Connection(std::move(sdBusIntfMock_), system_bus), sdbus::Error); ASSERT_THROW(Connection(std::move(sdBusIntfMock_), Connection::system_bus), sdbus::Error);
} }
TEST_F(ASessionBusConnection, ThrowsErrorWhenFlushingTheBusFailsDuringConstruction) TEST_F(ASessionBusConnection, ThrowsErrorWhenFlushingTheBusFailsDuringConstruction)
{ {
ON_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1))); ON_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
ON_CALL(*sdBusIntfMock_, sd_bus_flush(_)).WillByDefault(Return(-1)); ON_CALL(*sdBusIntfMock_, sd_bus_flush(_)).WillByDefault(Return(-1));
ASSERT_THROW(Connection(std::move(sdBusIntfMock_), session_bus), sdbus::Error); ASSERT_THROW(Connection(std::move(sdBusIntfMock_), Connection::session_bus), sdbus::Error);
} }
namespace namespace
@ -128,6 +153,10 @@ protected:
std::unique_ptr<Connection> con_; std::unique_ptr<Connection> con_;
}; };
template<> void AConnectionNameRequest<Connection::default_bus_t>::setUpBusOpenExpectation()
{
EXPECT_CALL(*sdBusIntfMock_, sd_bus_open(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
}
template<> void AConnectionNameRequest<Connection::system_bus_t>::setUpBusOpenExpectation() template<> void AConnectionNameRequest<Connection::system_bus_t>::setUpBusOpenExpectation()
{ {
EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_system(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1))); EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_system(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
@ -136,23 +165,40 @@ template<> void AConnectionNameRequest<Connection::session_bus_t>::setUpBusOpenE
{ {
EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1))); EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
} }
template<> void AConnectionNameRequest<Connection::custom_session_bus_t>::setUpBusOpenExpectation()
{
EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_user_with_address(_, _)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
}
template<> void AConnectionNameRequest<Connection::remote_system_bus_t>::setUpBusOpenExpectation() template<> void AConnectionNameRequest<Connection::remote_system_bus_t>::setUpBusOpenExpectation()
{ {
EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_system_remote(_, _)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1))); EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_system_remote(_, _)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
} }
template<> void AConnectionNameRequest<Connection::pseudo_bus_t>::setUpBusOpenExpectation()
{
EXPECT_CALL(*sdBusIntfMock_, sd_bus_new(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
// `sd_bus_start` for pseudo connection shall return an error value, remember this is a fake connection...
EXPECT_CALL(*sdBusIntfMock_, sd_bus_start(fakeBusPtr_)).WillOnce(Return(-EINVAL));
}
template <typename _BusTypeTag> template <typename _BusTypeTag>
std::unique_ptr<Connection> AConnectionNameRequest<_BusTypeTag>::makeConnection() std::unique_ptr<Connection> AConnectionNameRequest<_BusTypeTag>::makeConnection()
{ {
return std::make_unique<Connection>(std::unique_ptr<NiceMock<SdBusMock>>(sdBusIntfMock_), _BusTypeTag{}); return std::make_unique<Connection>(std::unique_ptr<NiceMock<SdBusMock>>(sdBusIntfMock_), _BusTypeTag{});
} }
template<> std::unique_ptr<Connection> AConnectionNameRequest<Connection::custom_session_bus_t>::makeConnection()
{
return std::make_unique<Connection>(std::unique_ptr<NiceMock<SdBusMock>>(sdBusIntfMock_), Connection::custom_session_bus, "custom session bus");
}
template<> std::unique_ptr<Connection> AConnectionNameRequest<Connection::remote_system_bus_t>::makeConnection() 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"); return std::make_unique<Connection>(std::unique_ptr<NiceMock<SdBusMock>>(sdBusIntfMock_), Connection::remote_system_bus, "some host");
} }
typedef ::testing::Types< Connection::system_bus_t typedef ::testing::Types< Connection::default_bus_t
, Connection::system_bus_t
, Connection::session_bus_t , Connection::session_bus_t
, Connection::custom_session_bus_t
, Connection::remote_system_bus_t , Connection::remote_system_bus_t
, Connection::pseudo_bus_t
> BusTypeTags; > BusTypeTags;
TYPED_TEST_SUITE(AConnectionNameRequest, BusTypeTags); TYPED_TEST_SUITE(AConnectionNameRequest, BusTypeTags);

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file Message_test.cpp * @file Message_test.cpp
* *
@ -238,3 +238,29 @@ TEST(AMessage, CanCarryAComplexType)
ASSERT_THAT(dataRead, Eq(dataWritten)); ASSERT_THAT(dataRead, Eq(dataWritten));
} }
TEST(AMessage, CanPeekASimpleType)
{
auto msg = sdbus::createPlainMessage();
msg << 123;
msg.seal();
std::string type;
std::string contents;
msg.peekType(type, contents);
ASSERT_THAT(type, "i");
ASSERT_THAT(contents, "");
}
TEST(AMessage, CanPeekContainerContents)
{
auto msg = sdbus::createPlainMessage();
msg << std::map<int, std::string>{{1, "one"}, {2, "two"}};
msg.seal();
std::string type;
std::string contents;
msg.peekType(type, contents);
ASSERT_THAT(type, "a");
ASSERT_THAT(contents, "{is}");
}

View File

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

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file Types_test.cpp * @file Types_test.cpp
* *
@ -230,6 +230,15 @@ TEST(AnObjectPath, CanBeConstructedFromStdString)
ASSERT_THAT(sdbus::ObjectPath{aPath}, Eq(aPath)); ASSERT_THAT(sdbus::ObjectPath{aPath}, Eq(aPath));
} }
TEST(AnObjectPath, CanBeMovedLikeAStdString)
{
std::string aPath{"/some/very/long/path/longer/than/sso"};
sdbus::ObjectPath oPath{aPath};
ASSERT_THAT(sdbus::ObjectPath{std::move(oPath)}, Eq(sdbus::ObjectPath(std::move(aPath))));
ASSERT_THAT(std::string(oPath), Eq(aPath));
}
TEST(ASignature, CanBeConstructedFromCString) TEST(ASignature, CanBeConstructedFromCString)
{ {
const char* aSignature = "us"; const char* aSignature = "us";
@ -244,6 +253,15 @@ TEST(ASignature, CanBeConstructedFromStdString)
ASSERT_THAT(sdbus::Signature{aSignature}, Eq(aSignature)); ASSERT_THAT(sdbus::Signature{aSignature}, Eq(aSignature));
} }
TEST(ASignature, CanBeMovedLikeAStdString)
{
std::string aSignature{"us"};
sdbus::Signature oSignature{aSignature};
ASSERT_THAT(sdbus::Signature{std::move(oSignature)}, Eq(sdbus::Signature(std::move(aSignature))));
ASSERT_THAT(std::string(oSignature), Eq(aSignature));
}
TEST(AUnixFd, DuplicatesAndOwnsFdUponStandardConstruction) TEST(AUnixFd, DuplicatesAndOwnsFdUponStandardConstruction)
{ {
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK); auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
@ -344,3 +362,21 @@ TEST(AUnixFd, TakesOverNewFdAndClosesOriginalFdOnAdoptingReset)
EXPECT_THAT(unixFd.get(), Eq(newFd)); EXPECT_THAT(unixFd.get(), Eq(newFd));
EXPECT_THAT(::close(fd), Eq(-1)); EXPECT_THAT(::close(fd), Eq(-1));
} }
TEST(AnError, CanBeConstructedFromANameAndAMessage)
{
auto error = sdbus::Error("name", "message");
EXPECT_THAT(error.getName(), Eq<std::string>("name"));
EXPECT_THAT(error.getMessage(), Eq<std::string>("message"));
}
TEST(AnError, CanBeConstructedFromANameOnly)
{
auto error1 = sdbus::Error("name");
auto error2 = sdbus::Error("name", nullptr);
EXPECT_THAT(error1.getName(), Eq<std::string>("name"));
EXPECT_THAT(error2.getName(), Eq<std::string>("name"));
EXPECT_THAT(error1.getMessage(), Eq<std::string>(""));
EXPECT_THAT(error2.getMessage(), Eq<std::string>(""));
}

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file SdBusMock.h * @file SdBusMock.h
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru) * @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@ -57,8 +57,10 @@ public:
MOCK_METHOD3(sd_bus_emit_interfaces_added_strv, int(sd_bus *bus, const char *path, char **interfaces)); MOCK_METHOD3(sd_bus_emit_interfaces_added_strv, int(sd_bus *bus, const char *path, char **interfaces));
MOCK_METHOD3(sd_bus_emit_interfaces_removed_strv, int(sd_bus *bus, const char *path, char **interfaces)); MOCK_METHOD3(sd_bus_emit_interfaces_removed_strv, int(sd_bus *bus, const char *path, char **interfaces));
MOCK_METHOD1(sd_bus_open_user, int(sd_bus **ret)); MOCK_METHOD1(sd_bus_open, int(sd_bus **ret));
MOCK_METHOD1(sd_bus_open_system, int(sd_bus **ret)); MOCK_METHOD1(sd_bus_open_system, int(sd_bus **ret));
MOCK_METHOD1(sd_bus_open_user, int(sd_bus **ret));
MOCK_METHOD2(sd_bus_open_user_with_address, int(sd_bus **ret, const char* address));
MOCK_METHOD2(sd_bus_open_system_remote, int(sd_bus **ret, const char *host)); 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_METHOD3(sd_bus_request_name, int(sd_bus *bus, const char *name, uint64_t flags));
MOCK_METHOD2(sd_bus_release_name, int(sd_bus *bus, const char *name)); MOCK_METHOD2(sd_bus_release_name, int(sd_bus *bus, const char *name));
@ -68,11 +70,28 @@ public:
MOCK_METHOD5(sd_bus_add_match, int(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata)); MOCK_METHOD5(sd_bus_add_match, int(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata));
MOCK_METHOD1(sd_bus_slot_unref, sd_bus_slot*(sd_bus_slot *slot)); MOCK_METHOD1(sd_bus_slot_unref, sd_bus_slot*(sd_bus_slot *slot));
MOCK_METHOD1(sd_bus_new, int(sd_bus **ret));
MOCK_METHOD1(sd_bus_start, int(sd_bus *bus));
MOCK_METHOD2(sd_bus_process, int(sd_bus *bus, sd_bus_message **r)); MOCK_METHOD2(sd_bus_process, int(sd_bus *bus, sd_bus_message **r));
MOCK_METHOD2(sd_bus_get_poll_data, int(sd_bus *bus, PollData* data)); MOCK_METHOD2(sd_bus_get_poll_data, int(sd_bus *bus, PollData* data));
MOCK_METHOD1(sd_bus_flush, int(sd_bus *bus)); MOCK_METHOD1(sd_bus_flush, int(sd_bus *bus));
MOCK_METHOD1(sd_bus_flush_close_unref, sd_bus *(sd_bus *bus)); MOCK_METHOD1(sd_bus_flush_close_unref, sd_bus *(sd_bus *bus));
MOCK_METHOD1(sd_bus_close_unref, sd_bus *(sd_bus *bus));
MOCK_METHOD2(sd_bus_message_set_destination, int(sd_bus_message *m, const char *destination));
MOCK_METHOD3(sd_bus_query_sender_creds, int(sd_bus_message *, uint64_t, sd_bus_creds **));
MOCK_METHOD1(sd_bus_creds_unref, sd_bus_creds*(sd_bus_creds *));
MOCK_METHOD2(sd_bus_creds_get_pid, int(sd_bus_creds *, pid_t *));
MOCK_METHOD2(sd_bus_creds_get_uid, int(sd_bus_creds *, uid_t *));
MOCK_METHOD2(sd_bus_creds_get_euid, int(sd_bus_creds *, uid_t *));
MOCK_METHOD2(sd_bus_creds_get_gid, int(sd_bus_creds *, gid_t *));
MOCK_METHOD2(sd_bus_creds_get_egid, int(sd_bus_creds *, gid_t *));
MOCK_METHOD2(sd_bus_creds_get_supplementary_gids, int(sd_bus_creds *, const gid_t **));
MOCK_METHOD2(sd_bus_creds_get_selinux_context, int(sd_bus_creds *, const char **));
}; };
#endif //SDBUS_CXX_SDBUS_MOCK_H #endif //SDBUS_CXX_SDBUS_MOCK_H

View File

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

View File

@ -4,7 +4,7 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
project(sdbus-c++-tools) project(sdbus-c++-tools VERSION 1.2.0)
include(GNUInstallDirs) include(GNUInstallDirs)
@ -43,9 +43,33 @@ set(CMAKE_CXX_STANDARD 14)
add_executable(sdbus-c++-xml2cpp ${SDBUSCPP_XML2CPP_SRCS}) add_executable(sdbus-c++-xml2cpp ${SDBUSCPP_XML2CPP_SRCS})
target_link_libraries (sdbus-c++-xml2cpp ${EXPAT_LIBRARIES}) target_link_libraries (sdbus-c++-xml2cpp ${EXPAT_LIBRARIES})
target_include_directories(sdbus-c++-xml2cpp PRIVATE ${EXPAT_INCLUDE_DIRS})
#---------------------------------- #----------------------------------
# INSTALLATION # INSTALLATION
#---------------------------------- #----------------------------------
install(TARGETS sdbus-c++-xml2cpp EXPORT sdbus-c++-xml2cpp DESTINATION ${CMAKE_INSTALL_BINDIR}) install(TARGETS sdbus-c++-xml2cpp EXPORT sdbus-c++-tools-targets DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT dev)
#----------------------------------
# CMAKE CONFIG & PACKAGE CONFIG
#----------------------------------
include(CMakePackageConfigHelpers)
install(EXPORT sdbus-c++-tools-targets
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++-tools
NAMESPACE SDBusCpp::
COMPONENT dev)
configure_package_config_file(cmake/sdbus-c++-tools-config.cmake.in cmake/sdbus-c++-tools-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++)
write_basic_package_version_file(cmake/sdbus-c++-tools-config-version.cmake COMPATIBILITY SameMajorVersion)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-tools-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-tools-config-version.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++-tools
COMPONENT dev)
configure_file(pkgconfig/sdbus-c++-tools.pc.in pkgconfig/sdbus-c++-tools.pc @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/sdbus-c++-tools.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT dev)

View File

@ -0,0 +1,4 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake")
check_required_components("sdbus-c++-xml2cpp")

View File

@ -0,0 +1,8 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
Name: @PROJECT_NAME@
Description: Stub generator tool for sdbus-c++ library
Version: @SDBUSCPP_VERSION@

View File

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

View File

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

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file BaseGenerator.cpp * @file BaseGenerator.cpp
* *
@ -182,4 +182,3 @@ std::string BaseGenerator::outArgsToType(const Nodes& args, bool bareList) const
return retTypeSS.str(); return retTypeSS.str();
} }

View File

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

View File

@ -1,6 +1,6 @@
/** /**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com> * (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* *
* @file ProxyGenerator.cpp * @file ProxyGenerator.cpp
* *
@ -33,6 +33,7 @@
#include <cstdlib> #include <cstdlib>
#include <algorithm> #include <algorithm>
#include <iterator> #include <iterator>
#include <regex>
using std::endl; using std::endl;
@ -130,6 +131,8 @@ std::string ProxyGenerator::processInterface(Node& interface) const
std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes& methods) const std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes& methods) const
{ {
const std::regex patternTimeout{R"(^(\d+)(min|s|ms|us)?$)"};
std::ostringstream definitionSS, asyncDeclarationSS; std::ostringstream definitionSS, asyncDeclarationSS;
for (const auto& method : methods) for (const auto& method : methods)
@ -143,6 +146,7 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
bool dontExpectReply{false}; bool dontExpectReply{false};
bool async{false}; bool async{false};
std::string timeoutValue; std::string timeoutValue;
std::smatch smTimeout;
Nodes annotations = (*method)["annotation"]; Nodes annotations = (*method)["annotation"];
for (const auto& annotation : annotations) for (const auto& annotation : annotations)
@ -168,6 +172,13 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
timeoutValue.clear(); timeoutValue.clear();
} }
if (!timeoutValue.empty() && !std::regex_match(timeoutValue, smTimeout, patternTimeout))
{
std::cerr << "Function: " << name << ": ";
std::cerr << "Option 'org.freedesktop.DBus.Method.Timeout' has unsupported timeout value! Option ignored..." << std::endl;
timeoutValue.clear();
}
auto retType = outArgsToType(outArgs); auto retType = outArgsToType(outArgs);
std::string inArgStr, inArgTypeStr; std::string inArgStr, inArgTypeStr;
std::tie(inArgStr, inArgTypeStr, std::ignore, std::ignore) = argsToNamesAndTypes(inArgs); std::tie(inArgStr, inArgTypeStr, std::ignore, std::ignore) = argsToNamesAndTypes(inArgs);
@ -193,7 +204,9 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
if (!timeoutValue.empty()) if (!timeoutValue.empty())
{ {
definitionSS << ".withTimeout(" << timeoutValue << "us)"; const auto val = smTimeout.str(1);
const auto unit = smTimeout.str(2);
definitionSS << ".withTimeout(" << val << (unit.empty() ? "us" : unit) << ")";
} }
if (inArgs.size() > 0) if (inArgs.size() > 0)

View File

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

View File

@ -85,6 +85,15 @@ static void _parse_signature(const std::string &signature, std::string &type, un
break; break;
} }
case '\0':
{
std::cerr <<
"Invalid array definition. Type is missing after '" << signature
<< "'."
<< std::endl;
exit(-1);
}
default: default:
{ {
type += "std::vector<"; type += "std::vector<";

View File

@ -292,12 +292,12 @@ void Document::Expat::character_data_handler(void* data, const XML_Char* chars,
nod = &(nod->children.back()); nod = &(nod->children.back());
} }
int x = 0, y = len - 1; int offset = 0, count = len;
while (isspace(chars[y]) && y > 0) --y; while (count > 0 && isspace(chars[count - 1])) --count;
while (isspace(chars[x]) && x < y) ++x; while (offset < count && isspace(chars[offset])) { ++offset; --count; }
nod->cdata = std::string(chars, x, y + 1); nod->cdata = std::string{chars + offset, static_cast<std::string::size_type>(count)};
} }
void Document::Expat::end_element_handler(void* data, const XML_Char* /*name*/) void Document::Expat::end_element_handler(void* data, const XML_Char* /*name*/)

View File

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