diff --git a/.clang-tidy b/.clang-tidy index 1cb4f15..e437416 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -7,7 +7,8 @@ Checks: modernize-*, performance-*, portability-*, - readability-* + readability-*, + -readability-function-cognitive-complexity HeaderFilterRegex: 'src' FormatStyle: file @@ -52,9 +53,9 @@ CheckOptions: - key: readability-identifier-naming.PrivateMethodCase value: lower_case - key: readability-identifier-naming.PublicMethodCase - value: camelCase + value: camelBack - key: readability-identifier-naming.ProtectedMethodCase - value: camelCase + value: camelBack - key: readability-identifier-naming.ConstexprVariableCase value: UPPER_CASE diff --git a/.github/workflows/check-code-formatting.yml b/.github/workflows/check-code-formatting.yml deleted file mode 100644 index 6eab6d9..0000000 --- a/.github/workflows/check-code-formatting.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: clang-format Check - -on: [pull_request] - -jobs: - formatting-check: - name: Formatting Check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Run clang-format style check for C/C++/Protobuf programs. - uses: jidicula/clang-format-action@v4.14.0 - with: - clang-format-version: "15" diff --git a/.github/workflows/code-format.yml b/.github/workflows/code-format.yml index b007929..0dcc9b5 100644 --- a/.github/workflows/code-format.yml +++ b/.github/workflows/code-format.yml @@ -18,26 +18,31 @@ jobs: uses: actions/checkout@v4 # --- C/C++ --- - - name: Format C/C++ with clang-format + - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y clang-format + sudo apt-get install -y clang-format pipx jq + + - name: Format C/C++ with clang-format + run: | find . -regex '.*\.\(cpp\|hpp\|cu\|cuh\|c\|h\)' -exec clang-format -style=file -i {} \; # --- CMake --- - name: Format CMake files run: | - sudo apt-get update - sudo apt-get install -y pipx pipx install cmakelang find . -regex '.*\.\(cmake\)' -exec cmake-format -i {} \; find . -name 'CMakeLists.txt' -exec cmake-format -i {} \; + # --- Markdown --- + - name: Format CMake files + run: | + pipx install mdformat + find . -type f -name "*.md" -exec mdformat {} + + # --- JSON --- - name: Format JSON files run: | - sudo apt-get update - sudo apt-get install -y jq find . -regex '.*\.\(json\)' -exec bash -c "jq . {} > {}.tmp && mv {}.tmp {}" \; # --- Create Pull Request --- diff --git a/.github/workflows/conf-build-test.yml b/.github/workflows/conf-build-test.yml index 1aee068..50b5f49 100644 --- a/.github/workflows/conf-build-test.yml +++ b/.github/workflows/conf-build-test.yml @@ -74,58 +74,3 @@ jobs: path: | ${{ runner.temp }}/common/* - release-deploy: - if: ${{ startsWith(github.ref, 'refs/tags/v') }} - needs: [build_test_package] - runs-on: ubuntu-latest - permissions: - contents: write - pages: write - id-token: write - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: actions/download-artifact@v4 - with: - path: artifacts - - - name: Move docs to website dir - run: mv artifacts/common/html github-pages - - - name: Clean packages - run: | - rm -rf artifacts/*/_CPack_Packages - mv artifacts/common . || true - - - name: Create CHANGELOG - run: | - git log $(git rev-list --tags --skip=1 --max-count=1 | xargs git describe --tags --abbrev=0)..$(git describe --tags --abbrev=0) \ - --pretty=format:"* %h - %s (%an, %ar)" > CHANGELOG.md - - - name: Package GitHub Page content - working-directory: ${{ github.workspace }}/github-pages - run: | - cmake -E make_directory ${{ runner.temp }}/page-packages - cmake -E tar c ${{ runner.temp }}/page-packages/github-pages.tar -- . - - - name: Release - uses: softprops/action-gh-release@v2 - with: - files: ./artifacts/*/* - body_path: ./CHANGELOG.md - - - uses: actions/upload-artifact@v4 - with: - name: github-pages - path: ${{ runner.temp }}/page-packages/* - - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 - diff --git a/.github/workflows/recreate-develop.yml b/.github/workflows/recreate-develop.yml deleted file mode 100644 index fd9ddfa..0000000 --- a/.github/workflows/recreate-develop.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Auto-Create Develop Branch -run-name: Recreating develop branch - -on: - pull_request_target: - types: [closed] - branches: [main] - -jobs: - create-develop-branch: - if: ${{ github.event.pull_request.merged == true && startsWith(github.base_ref, 'main') && startsWith(github.head_ref, 'develop') }} - runs-on: ubuntu-latest - permissions: - contents: write - - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - - name: Set Up Git - run: | - git config --global user.name "${{ github.actor }}" - git config --global user.email "${{ github.actor }}@users.noreply.github.com" - - - name: Delete existing 'develop' branch (if exists) - run: | - git fetch origin - git push origin --delete develop || echo "Branch 'develop' does not exist on remote." - - - name: Create new 'develop' branch from main - run: | - git checkout main - git checkout -b develop - git push origin develop - diff --git a/.github/workflows/release-deploy.yml b/.github/workflows/release-deploy.yml new file mode 100644 index 0000000..5503c30 --- /dev/null +++ b/.github/workflows/release-deploy.yml @@ -0,0 +1,60 @@ +name: release-deploy + +on: + workflow_run: + workflows: ["push-build-test-lint-release"] + types: + - completed + +jobs: + release: + if: ${{ github.event.workflow_run.conclusion == 'success' && startsWith(github.ref, 'refs/tags/v') }} + runs-on: ubuntu-latest + permissions: + contents: write + pages: write + id-token: write + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Move docs to website dir + run: mv artifacts/common/html github-pages + + - name: Clean packages + run: | + rm -rf artifacts/*/_CPack_Packages + mv artifacts/common . || true + + - name: Create CHANGELOG + run: | + git log $(git rev-list --tags --skip=1 --max-count=1 | xargs git describe --tags --abbrev=0)..$(git describe --tags --abbrev=0) \ + --pretty=format:"* %h - %s (%an, %ar)" > CHANGELOG.md + + - name: Package GitHub Page content + working-directory: ${{ github.workspace }}/github-pages + run: | + cmake -E make_directory ${{ runner.temp }}/page-packages + cmake -E tar c ${{ runner.temp }}/page-packages/github-pages.tar -- . + + - name: Release + uses: softprops/action-gh-release@v2 + with: + files: ./artifacts/*/* + body_path: ./CHANGELOG.md + + - uses: actions/upload-artifact@v4 + with: + name: github-pages + path: ${{ runner.temp }}/page-packages/* + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + diff --git a/.gitignore b/.gitignore index 37bf44f..195a574 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,3 @@ build* doc tags CMakeUserPresets.json -dbus_xml/net.connman/*.h -dbus_xml/net.connman/*.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 34154fe..97050af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ project( LANGUAGES C CXX) set_property(GLOBAL PROPERTY USE_FOLDERS ON) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) include(GNUInstallDirs) diff --git a/examples/connmanctl.cpp b/examples/connmanctl.cpp index 0f36325..c943fa6 100644 --- a/examples/connmanctl.cpp +++ b/examples/connmanctl.cpp @@ -77,6 +77,24 @@ auto main() -> int { props.print(); } } + } + if (cmd == "agent") { + auto on_register = [arg](const auto success) { + if (success) { + std::cout << "Agent " + << ((arg == "on") ? "registered" : "unregistered") + << "\n"; + } + }; + if (arg == "on") { + manager->registerAgent(manager->internalAgentPath(), + on_register); + } else if (arg == "off") { + manager->unregisterAgent(manager->internalAgentPath(), + on_register); + } else { + std::cout << "Usage: agent on/off\n"; + } } else if (cmd == "services") { const auto services = manager->services(); if (services.empty()) { @@ -222,7 +240,9 @@ auto main() -> int { !connect) { std::cout << (connect ? "Connecting" : "Disconnecting") << " to service: " << name << "\n"; - const auto onConnect = [name, connect](bool success) { + const auto on_connect = [name, connect, &connecting, + &cin_mutex, + &cin_cv](bool success) { if (success) { std::cout << "Service " << name @@ -233,12 +253,17 @@ auto main() -> int { << (connect ? " connect" : " disconnect") << " to service " << name << "\n"; } + { + const std::unique_lock lock(cin_mutex); + connecting = false; + } + cin_cv.notify_one(); }; if (connect) { - (*iterator)->connect(onConnect); + (*iterator)->connect(on_connect); connecting = true; } else { - (*iterator)->disconnect(onConnect); + (*iterator)->disconnect(on_connect); } } else { std::cout << "Service " << name << " is already " diff --git a/include/amarula/dbus/connman/gagent.hpp b/include/amarula/dbus/connman/gagent.hpp index 31d775b..30363cd 100644 --- a/include/amarula/dbus/connman/gagent.hpp +++ b/include/amarula/dbus/connman/gagent.hpp @@ -3,6 +3,7 @@ #include #include +#include #include namespace Amarula::DBus::G::Connman { @@ -11,10 +12,11 @@ class Connman; class Agent { GDBusNodeInfo *node_info_; guint registration_id_{0}; - GDBusConnection *connection_{nullptr}; - static constexpr const auto AGENT_PATH{"/net/amarula/gconnman/agent"}; + GDBusConnection* connection_{nullptr}; + std::string path_{"/net/amarula/gconnman/agent"}; - explicit Agent(GDBusConnection *connection); + explicit Agent(GDBusConnection* connection, + const std::string& path = std::string()); using RequestInputCallback = std::function; diff --git a/include/amarula/dbus/connman/gmanager.hpp b/include/amarula/dbus/connman/gmanager.hpp index af70093..e7d6393 100644 --- a/include/amarula/dbus/connman/gmanager.hpp +++ b/include/amarula/dbus/connman/gmanager.hpp @@ -123,6 +123,8 @@ class Manager : public DBusProxy { void onTechnologiesChanged(OnTechListChangedCallback callback); void onServicesChanged(OnServListChangedCallback callback); + auto internalAgentPath() const -> std::string { return agent_->path_; }; + private: enum class InputType : std::uint8_t { InputUnknown = 0, @@ -152,8 +154,7 @@ class Manager : public DBusProxy { OnServicesChangedCallback services_changed_cb_{ [](const Manager::ProxyList&) {}}; - explicit Manager(DBus* dbus); - void init(); + explicit Manager(DBus* dbus, const std::string& agent_path = std::string()); using DBusProxy::DBusProxy; diff --git a/include/amarula/dbus/connman/gservice.hpp b/include/amarula/dbus/connman/gservice.hpp index 06204dc..56b696e 100644 --- a/include/amarula/dbus/connman/gservice.hpp +++ b/include/amarula/dbus/connman/gservice.hpp @@ -214,6 +214,7 @@ struct ServProperties { Type type_{}; std::optional> security_; std::optional> name_servers_; + std::optional> name_servers_conf_; std::optional> domains_; std::optional> time_servers_; bool autoconnect_{false}; @@ -246,6 +247,8 @@ class Service : public DBusProxy { void remove(PropertiesSetCallback callback = nullptr); void setAutoconnect(bool autoconnect, PropertiesSetCallback callback = nullptr); + void setNameServers(const std::vector& name_servers, + PropertiesSetCallback callback = nullptr); friend class Manager; }; diff --git a/include/amarula/dbus/connman/gtechnology.hpp b/include/amarula/dbus/connman/gtechnology.hpp index e5fb49f..c11e24f 100644 --- a/include/amarula/dbus/connman/gtechnology.hpp +++ b/include/amarula/dbus/connman/gtechnology.hpp @@ -28,7 +28,7 @@ struct TechProperties { [[nodiscard]] auto isPowered() const { return powered_; } [[nodiscard]] auto isConnected() const { return connected_; } [[nodiscard]] auto isTethering() const { return tethering_; } - [[nodiscard]] auto getTetheringFreq() const { return tetheringFreq_; } + [[nodiscard]] auto getTetheringFreq() const { return tethering_freq_; } void print() const; private: @@ -37,7 +37,7 @@ struct TechProperties { bool tethering_ = false; std::string name_; Type type_; - int tetheringFreq_{0}; + int tethering_freq_{0}; void update(const gchar* key, GVariant* value); diff --git a/include/amarula/dbus/gdbus.hpp b/include/amarula/dbus/gdbus.hpp index 6eec08e..157561c 100644 --- a/include/amarula/dbus/gdbus.hpp +++ b/include/amarula/dbus/gdbus.hpp @@ -31,8 +31,8 @@ class DBus { virtual ~DBus(); void start(); - void on_any_async_done(); - void on_any_async_start(); + void onAnyAsyncDone(); + void onAnyAsyncStart(); [[nodiscard]] auto connection() const { return connection_; } }; diff --git a/include/amarula/dbus/gproxy.hpp b/include/amarula/dbus/gproxy.hpp index 5fd9b17..54a0bc9 100644 --- a/include/amarula/dbus/gproxy.hpp +++ b/include/amarula/dbus/gproxy.hpp @@ -47,6 +47,40 @@ class DBusProxy : public std::enable_shared_from_this> { g_variant_unref(value); } + static void on_properties_changed_cb( + GDBusProxy* /*proxy*/, gchar* /*sender_name*/, gchar* /*signal_name*/, + GVariant* parameters /*string name, variant value*/, + gpointer user_data) { + auto self = static_cast(user_data); + self->update_property(parameters); + if (self->on_property_changed_user_cb_) { + std::lock_guard const lock(self->cb_mtx_); + self->on_property_changed_user_cb_(self->props_); + } + } + + static void get_property_cb(GObject* proxy, GAsyncResult* res, + gpointer user_data) { + GError* error = nullptr; + GVariant* out_properties = nullptr; + std::unique_ptr data( + static_cast(user_data)); + auto self = data->getSelf(); + const auto counter = data->getCounter(); + const auto success = + finish(G_DBUS_PROXY(proxy), res, &error, &out_properties); + + if (success) { + self->updateProperties(out_properties); + g_variant_unref(out_properties); + } else { + std::cerr << error->message << '\n'; + g_error_free(error); + } + self->template executeCallBack(counter, + self->props_); + } + protected: void updateProperties(GVariant* properties) { GVariantIter* iter = g_variant_iter_new(properties); @@ -73,18 +107,6 @@ class DBusProxy : public std::enable_shared_from_this> { return false; } - static void on_properties_changed_cb( - GDBusProxy* /*proxy*/, gchar* /*sender_name*/, gchar* /*signal_name*/, - GVariant* parameters /*string name, variant value*/, - gpointer user_data) { - auto self = static_cast(user_data); - self->update_property(parameters); - if (self->on_property_changed_user_cb_) { - std::lock_guard const lock(self->cb_mtx_); - self->on_property_changed_user_cb_(self->props_); - } - } - public: DBusProxy(const DBusProxy&) = delete; auto operator=(const DBusProxy&) = delete; @@ -112,8 +134,8 @@ class DBusProxy : public std::enable_shared_from_this> { void getProperties(PropertiesCallback callback = nullptr) { auto data = prepareCallback(std::move(callback)); - call_method(proxy_, nullptr, "GetProperties", nullptr, - &DBusProxy::get_property_cb, data.release()); + callMethod(proxy_, nullptr, "GetProperties", nullptr, + &DBusProxy::get_property_cb, data.release()); } void onPropertyChanged(const PropertiesCallback& callback) { @@ -150,7 +172,7 @@ class DBusProxy : public std::enable_shared_from_this> { callbacks_.erase(counter.value()); } - dbus_->on_any_async_done(); + dbus_->onAnyAsyncDone(); } explicit DBusProxy(DBus* dbus, const gchar* name, const gchar* obj_path, @@ -175,7 +197,7 @@ class DBusProxy : public std::enable_shared_from_this> { template auto prepareCallback(T callback) { std::lock_guard const lock(mtx_); - dbus_->on_any_async_start(); + dbus_->onAnyAsyncStart(); std::optional counter{std::nullopt}; if (callback) { size_t index = ++callback_counter_; @@ -203,31 +225,9 @@ class DBusProxy : public std::enable_shared_from_this> { self->template executeCallBack(counter, success); } - static void get_property_cb(GObject* proxy, GAsyncResult* res, - gpointer user_data) { - GError* error = nullptr; - GVariant* out_properties = nullptr; - std::unique_ptr data( - static_cast(user_data)); - auto self = data->getSelf(); - const auto counter = data->getCounter(); - const auto success = - finish(G_DBUS_PROXY(proxy), res, &error, &out_properties); - - if (success) { - self->updateProperties(out_properties); - g_variant_unref(out_properties); - } else { - std::cerr << error->message << '\n'; - g_error_free(error); - } - self->template executeCallBack(counter, - self->props_); - } - - static void set_property(GDBusProxy* proxy, const gchar* arg_name, - GVariant* arg_value, GCancellable* cancellable, - GAsyncReadyCallback callback, gpointer user_data + static void setProperty(GDBusProxy* proxy, const gchar* arg_name, + GVariant* arg_value, GCancellable* cancellable, + GAsyncReadyCallback callback, gpointer user_data ) { std::array tuple_elements{ @@ -239,9 +239,9 @@ class DBusProxy : public std::enable_shared_from_this> { user_data); } - static void call_method(GDBusProxy* proxy, GCancellable* cancellable, - const gchar* arg_name, GVariant* parameters, - GAsyncReadyCallback callback, gpointer user_data) { + static void callMethod(GDBusProxy* proxy, GCancellable* cancellable, + const gchar* arg_name, GVariant* parameters, + GAsyncReadyCallback callback, gpointer user_data) { g_dbus_proxy_call( proxy, arg_name, (parameters != nullptr) ? parameters diff --git a/src/dbus/gconnman.cpp b/src/dbus/gconnman.cpp index a6f3776..f03f823 100644 --- a/src/dbus/gconnman.cpp +++ b/src/dbus/gconnman.cpp @@ -18,7 +18,6 @@ Connman::Connman() clock_{std::shared_ptr(new Clock(dbus_.get()))}, manager_{std::shared_ptr(new Manager(dbus_.get()))} { clock_->getProperties(); - manager_->init(); } } // namespace Amarula::DBus::G::Connman diff --git a/src/dbus/gconnman_agent.cpp b/src/dbus/gconnman_agent.cpp index 3f46a97..ec43a70 100644 --- a/src/dbus/gconnman_agent.cpp +++ b/src/dbus/gconnman_agent.cpp @@ -27,7 +27,11 @@ static constexpr const char *INTROSPECTION_XML = " " ""; -Agent::Agent(GDBusConnection *connection) : connection_{connection} { +Agent::Agent(GDBusConnection *connection, const std::string &path) + : connection_{connection} { + if (!path.empty()) { + path_ = path; + } GError *err = nullptr; node_info_ = g_dbus_node_info_new_for_xml(INTROSPECTION_XML, &err); if (node_info_ == nullptr) { @@ -38,7 +42,7 @@ Agent::Agent(GDBusConnection *connection) : connection_{connection} { } registration_id_ = g_dbus_connection_register_object( - connection, AGENT_PATH, *(node_info_->interfaces), &INTERFACE_VTABLE, + connection, path_.c_str(), *(node_info_->interfaces), &INTERFACE_VTABLE, this, nullptr, &err); if (registration_id_ == 0) { diff --git a/src/dbus/gconnman_clock.cpp b/src/dbus/gconnman_clock.cpp index 269c68b..9b3bf26 100644 --- a/src/dbus/gconnman_clock.cpp +++ b/src/dbus/gconnman_clock.cpp @@ -31,11 +31,11 @@ void ClockProperties::update(const gchar* key, GVariant* value) { time_ = g_variant_get_uint64(value); } else if (g_strcmp0(key, TIMEUPDATES_STR) == 0U) { time_updates_ = - TIME_UPDATE_MAP.from_string(g_variant_get_string(value, nullptr)); + TIME_UPDATE_MAP.fromString(g_variant_get_string(value, nullptr)); } else if (g_strcmp0(key, TIMEZONE_STR) == 0U) { timezone_ = g_variant_get_string(value, nullptr); } else if (g_strcmp0(key, TIMEZONEUPDATES_STR) == 0U) { - timezone_updates_ = TIME_ZONE_UPDATE_MAP.from_string( + timezone_updates_ = TIME_ZONE_UPDATE_MAP.fromString( g_variant_get_string(value, nullptr)); } else if (g_strcmp0(key, TIMESERVERS_STR) == 0U) { time_servers_ = as_to_vector(value); @@ -51,23 +51,23 @@ Clock::Clock(DBus* dbus) void Clock::setTime(uint64_t time, PropertiesSetCallback callback) { auto data = prepareCallback(std::move(callback)); - set_property(proxy(), TIME_STR, g_variant_new_uint64(time), nullptr, - &Clock::finishAsyncCall, data.release()); + setProperty(proxy(), TIME_STR, g_variant_new_uint64(time), nullptr, + &Clock::finishAsyncCall, data.release()); } void Clock::setTimeZone(const std::string& timezone, PropertiesSetCallback callback) { auto data = prepareCallback(std::move(callback)); - set_property(proxy(), TIMEZONE_STR, g_variant_new_string(timezone.c_str()), - nullptr, &Clock::finishAsyncCall, data.release()); + setProperty(proxy(), TIMEZONE_STR, g_variant_new_string(timezone.c_str()), + nullptr, &Clock::finishAsyncCall, data.release()); } void Clock::setTimeUpdates(const Properties::TimeUpdate time_updates, PropertiesSetCallback callback) { auto data = prepareCallback(std::move(callback)); - set_property( + setProperty( proxy(), TIMEUPDATES_STR, - g_variant_new_string((TIME_UPDATE_MAP.to_string(time_updates)).data()), + g_variant_new_string((TIME_UPDATE_MAP.toString(time_updates)).data()), nullptr, &Clock::finishAsyncCall, data.release()); } @@ -75,11 +75,10 @@ void Clock::setTimeZoneUpdates( const Properties::TimeZoneUpdate time_zone_updates, PropertiesSetCallback callback) { auto data = prepareCallback(std::move(callback)); - set_property( - proxy(), TIMEZONEUPDATES_STR, - g_variant_new_string( - (TIME_ZONE_UPDATE_MAP.to_string(time_zone_updates)).data()), - nullptr, &Clock::finishAsyncCall, data.release()); + setProperty(proxy(), TIMEZONEUPDATES_STR, + g_variant_new_string( + (TIME_ZONE_UPDATE_MAP.toString(time_zone_updates)).data()), + nullptr, &Clock::finishAsyncCall, data.release()); } void Clock::setTimeServers(const std::vector& servers, @@ -92,8 +91,8 @@ void Clock::setTimeServers(const std::vector& servers, g_variant_builder_add_value(&builder, str_variant); } GVariant* servers_variant = g_variant_builder_end(&builder); - set_property(proxy(), TIMESERVERS_STR, servers_variant, nullptr, - &Clock::finishAsyncCall, data.release()); + setProperty(proxy(), TIMESERVERS_STR, servers_variant, nullptr, + &Clock::finishAsyncCall, data.release()); g_variant_builder_clear(&builder); } @@ -104,10 +103,10 @@ void ClockProperties::print() const { std::cout << std::put_time(std::localtime(&time_value), "%Y-%m-%d %H:%M:%S") << ")\n"; std::cout << TIMEUPDATES_STR << ": " - << TIME_UPDATE_MAP.to_string(time_updates_) << '\n'; + << TIME_UPDATE_MAP.toString(time_updates_) << '\n'; std::cout << TIMEZONE_STR << ": " << timezone_ << '\n'; std::cout << TIMEZONEUPDATES_STR << ": " - << TIME_ZONE_UPDATE_MAP.to_string(timezone_updates_) << '\n'; + << TIME_ZONE_UPDATE_MAP.toString(timezone_updates_) << '\n'; std::cout << TIMESERVERSYNCED_STR << ": " << std::boolalpha << time_server_synced_ << '\n'; std::cout << TIMESERVERS_STR << ": "; diff --git a/src/dbus/gconnman_manager.cpp b/src/dbus/gconnman_manager.cpp index 92d24fa..82bebc4 100644 --- a/src/dbus/gconnman_manager.cpp +++ b/src/dbus/gconnman_manager.cpp @@ -34,15 +34,16 @@ void ManaProperties::update(const gchar* key, GVariant* value) { if (g_strcmp0(key, OFFLINEMODE_STR) == 0) { offline_mode_ = (g_variant_get_boolean(value) == 1U); } else if (g_strcmp0(key, STATE_STR) == 0U) { - state_ = STATE_MAP.from_string(g_variant_get_string(value, nullptr)); + state_ = STATE_MAP.fromString(g_variant_get_string(value, nullptr)); } else { std::cerr << "Unknown property for Manager: " << key << '\n'; } } -Manager::Manager(DBus* dbus) +Manager::Manager(DBus* dbus, const std::string& agent_path) : DBusProxy(dbus, SERVICE, MANAGER_PATH, MANAGER_INTERFACE), - agent_{std::unique_ptr(new Agent(dbus->connection()))} { + agent_{ + std::unique_ptr(new Agent(dbus->connection(), agent_path))} { setup_agent(); get_technologies(); get_services(); @@ -195,14 +196,12 @@ void Manager::setup_agent() { }); } -void Manager::init() { registerAgent(agent_->AGENT_PATH); } - void Manager::setOfflineMode(bool offline_mode, PropertiesSetCallback callback) { auto data = prepareCallback(std::move(callback)); - set_property(proxy(), OFFLINEMODE_STR, - g_variant_new_boolean(static_cast(offline_mode)), - nullptr, &Manager::finishAsyncCall, data.release()); + setProperty(proxy(), OFFLINEMODE_STR, + g_variant_new_boolean(static_cast(offline_mode)), + nullptr, &Manager::finishAsyncCall, data.release()); } auto Manager::dict_to_path_prop(GVariant* tuple) @@ -277,13 +276,13 @@ void Manager::get_proxies_cb(GObject* proxy, GAsyncResult* res, } void Manager::get_technologies() { - call_method(proxy(), nullptr, GETTECHNOLOGIES_STR, nullptr, - &Manager::get_proxies_cb, this); + callMethod(proxy(), nullptr, GETTECHNOLOGIES_STR, nullptr, + &Manager::get_proxies_cb, this); } void Manager::get_services() { - call_method(proxy(), nullptr, GETSERVICES_STR, nullptr, - &Manager::get_proxies_cb, this); + callMethod(proxy(), nullptr, GETSERVICES_STR, nullptr, + &Manager::get_proxies_cb, this); } void Manager::registerAgent(const std::string& object_path, @@ -291,8 +290,8 @@ void Manager::registerAgent(const std::string& object_path, auto data = prepareCallback(std::move(callback)); GVariant* child = g_variant_new_object_path(object_path.c_str()); GVariant* parameters = g_variant_new_tuple(&child, 1); - call_method(proxy(), nullptr, REGISTERAGENT_STR, parameters, - &Manager::finishAsyncCall, data.release()); + callMethod(proxy(), nullptr, REGISTERAGENT_STR, parameters, + &Manager::finishAsyncCall, data.release()); } void Manager::unregisterAgent(const std::string& object_path, @@ -300,8 +299,8 @@ void Manager::unregisterAgent(const std::string& object_path, auto data = prepareCallback(std::move(callback)); GVariant* child = g_variant_new_object_path(object_path.c_str()); GVariant* parameters = g_variant_new_tuple(&child, 1); - call_method(proxy(), nullptr, UNREGISTERAGENT_STR, parameters, - &Manager::finishAsyncCall, data.release()); + callMethod(proxy(), nullptr, UNREGISTERAGENT_STR, parameters, + &Manager::finishAsyncCall, data.release()); } void Manager::on_technology_added_removed_cb(GDBusProxy* /*proxy*/, @@ -410,7 +409,7 @@ auto Manager::parse_fields(GVariant* fields) -> GPtrArray* { g_variant_iter_init(&inner_iter, inner_dict); while (g_variant_iter_next(&inner_iter, "{&s@v}", &prop_name, - &prop_value_variant)) { + &prop_value_variant) != 0) { auto* prop_value = g_variant_get_variant(prop_value_variant); if (g_strcmp0(prop_name, "Type") == 0) { desc->type = g_variant_dup_string(prop_value, nullptr); diff --git a/src/dbus/gconnman_private.hpp b/src/dbus/gconnman_private.hpp index 5a3730a..62a6d8b 100644 --- a/src/dbus/gconnman_private.hpp +++ b/src/dbus/gconnman_private.hpp @@ -61,6 +61,7 @@ constexpr auto DISCONNECT_STR = "Disconnect"; constexpr auto REMOVE_STR = "Remove"; constexpr auto INTERFACE_STR = "Interface"; constexpr auto MTU_STR = "MTU"; +constexpr auto NAMESERVERS_CONFIGURATION_STR = "Nameservers.Configuration"; // Manager interface constexpr auto MANAGER_INTERFACE = "net.connman.Manager"; diff --git a/src/dbus/gconnman_service.cpp b/src/dbus/gconnman_service.cpp index 8433af4..7bca6ec 100644 --- a/src/dbus/gconnman_service.cpp +++ b/src/dbus/gconnman_service.cpp @@ -89,34 +89,42 @@ Service::Service(DBus* dbus, const gchar* obj_path) void Service::connect(PropertiesSetCallback callback) { auto data = prepareCallback(std::move(callback)); - call_method(proxy(), nullptr, CONNECT_STR, nullptr, - &Service::finishAsyncCall, data.release()); + callMethod(proxy(), nullptr, CONNECT_STR, nullptr, + &Service::finishAsyncCall, data.release()); } void Service::disconnect(PropertiesSetCallback callback) { auto data = prepareCallback(std::move(callback)); - call_method(proxy(), nullptr, DISCONNECT_STR, nullptr, - &Service::finishAsyncCall, data.release()); + callMethod(proxy(), nullptr, DISCONNECT_STR, nullptr, + &Service::finishAsyncCall, data.release()); } void Service::remove(PropertiesSetCallback callback) { auto data = prepareCallback(std::move(callback)); - call_method(proxy(), nullptr, REMOVE_STR, nullptr, - &Service::finishAsyncCall, data.release()); + callMethod(proxy(), nullptr, REMOVE_STR, nullptr, &Service::finishAsyncCall, + data.release()); } void Service::setAutoconnect(const bool autoconnect, PropertiesSetCallback callback) { auto data = prepareCallback(std::move(callback)); - set_property(proxy(), AUTOCONNECT_STR, - g_variant_new_boolean(static_cast(autoconnect)), - nullptr, &Service::finishAsyncCall, data.release()); + setProperty(proxy(), AUTOCONNECT_STR, + g_variant_new_boolean(static_cast(autoconnect)), + nullptr, &Service::finishAsyncCall, data.release()); +} + +void Service::setNameServers(const std::vector& name_servers, + PropertiesSetCallback callback) { + auto data = prepareCallback(std::move(callback)); + auto variant = vector_to_as(name_servers); + setProperty(proxy(), NAMESERVERS_CONFIGURATION_STR, variant.get(), nullptr, + &Service::finishAsyncCall, data.release()); } void IPv4::update(const gchar* key, GVariant* value) { if (g_strcmp0(key, METHOD_STR) == 0U) { method_ = - IPV4_METHOD_MAP.from_string(g_variant_get_string(value, nullptr)); + IPV4_METHOD_MAP.fromString(g_variant_get_string(value, nullptr)); } else if (g_strcmp0(key, ADDRESS_STR) == 0U) { address_ = g_variant_get_string(value, nullptr); } else if (g_strcmp0(key, NETMASK_STR) == 0U) { @@ -131,14 +139,14 @@ void IPv4::update(const gchar* key, GVariant* value) { void IPv6::update(const gchar* key, GVariant* value) { if (g_strcmp0(key, METHOD_STR) == 0U) { method_ = - IPV6_METHOD_MAP.from_string(g_variant_get_string(value, nullptr)); + IPV6_METHOD_MAP.fromString(g_variant_get_string(value, nullptr)); } else if (g_strcmp0(key, ADDRESS_STR) == 0U) { address_ = g_variant_get_string(value, nullptr); } else if (g_strcmp0(key, GATEWAY_STR) == 0U) { gateway_ = g_variant_get_string(value, nullptr); } else if (g_strcmp0(key, PRIVACY_STR) == 0U) { privacy_ = - IPV6_PRIVACY_MAP.from_string(g_variant_get_string(value, nullptr)); + IPV6_PRIVACY_MAP.fromString(g_variant_get_string(value, nullptr)); } else if (g_strcmp0(key, PREFIXLENGTH_STR) == 0U) { prefix_length_ = static_cast(g_variant_get_byte(value)); } else { @@ -168,7 +176,7 @@ void GVariantParser::parse(GVariant* variant) { void Ethernet::update(const gchar* key, GVariant* value) { if (g_strcmp0(key, METHOD_STR) == 0U) { - method_ = ETHERNET_METHOD_MAP.from_string( + method_ = ETHERNET_METHOD_MAP.fromString( g_variant_get_string(value, nullptr)); } else if (g_strcmp0(key, INTERFACE_STR) == 0U) { interface_ = g_variant_get_string(value, nullptr); @@ -198,7 +206,7 @@ void Provider::update(const gchar* key, GVariant* value) { void Proxy::update(const gchar* key, GVariant* value) { if (g_strcmp0(key, METHOD_STR) == 0U) { method_ = - PROXY_METHOD_MAP.from_string(g_variant_get_string(value, nullptr)); + PROXY_METHOD_MAP.fromString(g_variant_get_string(value, nullptr)); } else if (g_strcmp0(key, URL_STR) == 0U) { url_ = g_variant_get_string(value, nullptr); } else if (g_strcmp0(key, SERVERS_STR) == 0U) { @@ -214,11 +222,11 @@ void ServProperties::update(const gchar* key, GVariant* value) { if (g_strcmp0(key, NAME_STR) == 0) { name_ = g_variant_get_string(value, nullptr); } else if (g_strcmp0(key, TYPE_STR) == 0U) { - type_ = TYPE_MAP.from_string(g_variant_get_string(value, nullptr)); + type_ = TYPE_MAP.fromString(g_variant_get_string(value, nullptr)); } else if (g_strcmp0(key, STATE_STR) == 0U) { - state_ = STATE_MAP.from_string(g_variant_get_string(value, nullptr)); + state_ = STATE_MAP.fromString(g_variant_get_string(value, nullptr)); } else if (g_strcmp0(key, ERROR_STR) == 0U) { - error_ = ERROR_MAP.from_string(g_variant_get_string(value, nullptr)); + error_ = ERROR_MAP.fromString(g_variant_get_string(value, nullptr)); } else if (g_strcmp0(key, FAVORITE_STR) == 0U) { favorite_ = g_variant_get_boolean(value) == 1U; } else if (g_strcmp0(key, IMMUTABLE_STR) == 0U) { @@ -259,6 +267,11 @@ void ServProperties::update(const gchar* key, GVariant* value) { (g_variant_n_children(value) != 0) ? std::optional>(as_to_vector(value)) : std::nullopt; + } else if (g_strcmp0(key, NAMESERVERS_CONFIGURATION_STR) == 0U) { + name_servers_conf_ = + (g_variant_n_children(value) != 0) + ? std::optional>(as_to_vector(value)) + : std::nullopt; } else if (g_strcmp0(key, DOMAINS_STR) == 0U) { domains_ = (g_variant_n_children(value) != 0) @@ -276,13 +289,13 @@ void ServProperties::update(const gchar* key, GVariant* value) { void ServProperties::print() const { std::cout << "@@@@@@@@@@ ServProperties: @@@@@@@@@@@@@@@\n"; - std::cout << "State: " << STATE_MAP.to_string(state_) << '\n'; + std::cout << "State: " << STATE_MAP.toString(state_) << '\n'; if (error_ != Error::None) { - std::cout << "Error: " << ERROR_MAP.to_string(error_) << '\n'; + std::cout << "Error: " << ERROR_MAP.toString(error_) << '\n'; } std::cout << "Name: " << name_ << '\n'; - std::cout << "Type: " << TYPE_MAP.to_string(type_) << '\n'; + std::cout << "Type: " << TYPE_MAP.toString(type_) << '\n'; std::cout << "Strength: " << static_cast(strength_) << '\n'; std::cout << "AutoConnect: " << std::boolalpha << autoconnect_ << '\n'; std::cout << "mDNS: " << mdns_ << '\n'; @@ -293,7 +306,7 @@ void ServProperties::print() const { if (security_) { std::cout << "Security: "; for (const auto& sec : security_.value()) { - std::cout << SECURITY_MAP.to_string(sec) << ' '; + std::cout << SECURITY_MAP.toString(sec) << ' '; } std::cout << '\n'; } @@ -306,6 +319,14 @@ void ServProperties::print() const { std::cout << '\n'; } + if (name_servers_conf_) { + std::cout << "Nameservers.Configuration: "; + for (const auto& nserver : name_servers_conf_.value()) { + std::cout << nserver << ' '; + } + std::cout << '\n'; + } + if (domains_) { std::cout << "Domains: "; for (const auto& domain : domains_.value()) { @@ -330,7 +351,9 @@ void ServProperties::print() const { ipv6_.value().print(); } - ethernet_.value().print(); + if (ethernet_) { + ethernet_.value().print(); + } if (provider_) { provider_.value().print(); @@ -345,7 +368,7 @@ void ServProperties::print() const { void Ethernet::print() const { std::cout << "Ethernet:\n"; - std::cout << " Method: " << ETHERNET_METHOD_MAP.to_string(method_) << '\n'; + std::cout << " Method: " << ETHERNET_METHOD_MAP.toString(method_) << '\n'; std::cout << " Interface: " << interface_ << '\n'; std::cout << " Address: " << address_ << '\n'; std::cout << " MTU: " << mtu_ << '\n'; @@ -353,7 +376,7 @@ void Ethernet::print() const { void IPv4::print() const { std::cout << "IPv4:\n"; - std::cout << " Method: " << IPV4_METHOD_MAP.to_string(method_) << '\n'; + std::cout << " Method: " << IPV4_METHOD_MAP.toString(method_) << '\n'; std::cout << " Address: " << address_ << '\n'; std::cout << " Netmask: " << netmask_ << '\n'; std::cout << " Gateway: " << gateway_ << '\n'; @@ -369,7 +392,7 @@ void Provider::print() const { void IPv6::print() const { std::cout << "IPv6:\n"; - std::cout << " Method: " << IPV6_METHOD_MAP.to_string(method_) << '\n'; + std::cout << " Method: " << IPV6_METHOD_MAP.toString(method_) << '\n'; std::cout << " Address: " << address_ << '\n'; std::cout << " Gateway: " << gateway_ << '\n'; std::cout << " Privacy: " << static_cast(privacy_) << '\n'; @@ -379,7 +402,7 @@ void IPv6::print() const { void Proxy::print() const { std::cout << "Proxy:\n"; - std::cout << " Method: " << PROXY_METHOD_MAP.to_string(method_) << '\n'; + std::cout << " Method: " << PROXY_METHOD_MAP.toString(method_) << '\n'; std::cout << " URL: " << url_ << '\n'; std::cout << " Servers: "; for (const auto& server : servers_) { diff --git a/src/dbus/gconnman_technology.cpp b/src/dbus/gconnman_technology.cpp index 2dfceb1..d210117 100644 --- a/src/dbus/gconnman_technology.cpp +++ b/src/dbus/gconnman_technology.cpp @@ -29,22 +29,22 @@ Technology::Technology(DBus* dbus, const gchar* obj_path) void Technology::setPowered(bool powered, PropertiesSetCallback callback) { auto data = prepareCallback(std::move(callback)); - set_property(proxy(), POWERED_STR, - g_variant_new_boolean(static_cast(powered)), nullptr, - &Technology::finishAsyncCall, data.release()); + setProperty(proxy(), POWERED_STR, + g_variant_new_boolean(static_cast(powered)), nullptr, + &Technology::finishAsyncCall, data.release()); } void Technology::scan(PropertiesSetCallback callback) { auto data = prepareCallback(std::move(callback)); - call_method(proxy(), nullptr, SCAN_STR, nullptr, - &Technology::finishAsyncCall, data.release()); + callMethod(proxy(), nullptr, SCAN_STR, nullptr, + &Technology::finishAsyncCall, data.release()); } void TechProperties::update(const gchar* key, GVariant* value) { if (g_strcmp0(key, NAME_STR) == 0) { name_ = g_variant_get_string(value, nullptr); } else if (g_strcmp0(key, TYPE_STR) == 0U) { - type_ = TYPE_MAP.from_string(g_variant_get_string(value, nullptr)); + type_ = TYPE_MAP.fromString(g_variant_get_string(value, nullptr)); } else if (g_strcmp0(key, POWERED_STR) == 0U) { powered_ = g_variant_get_boolean(value) == 1U; @@ -53,7 +53,7 @@ void TechProperties::update(const gchar* key, GVariant* value) { } else if (g_strcmp0(key, TETHERING_STR) == 0U) { tethering_ = g_variant_get_boolean(value) == 1U; } else if (g_strcmp0(key, TETHERINGFREQ_STR) == 0U) { - tetheringFreq_ = g_variant_get_int32(value); + tethering_freq_ = g_variant_get_int32(value); } else { std::cerr << "Unknown property for Technology: " << key << '\n'; } @@ -62,11 +62,11 @@ void TechProperties::update(const gchar* key, GVariant* value) { void TechProperties::print() const { std::cout << "@@@@@@@@@@ TechProperties: @@@@@@@@@@@@@@@\n"; std::cout << NAME_STR << ": " << name_ << "\n"; - std::cout << TYPE_STR << ": " << TYPE_MAP.to_string(type_) << "\n"; + std::cout << TYPE_STR << ": " << TYPE_MAP.toString(type_) << "\n"; std::cout << POWERED_STR << ": " << std::boolalpha << powered_ << "\n"; std::cout << CONNECTED_STR << ": " << std::boolalpha << connected_ << "\n"; std::cout << TETHERING_STR << ": " << std::boolalpha << tethering_ << "\n"; - std::cout << TETHERINGFREQ_STR << ": " << tetheringFreq_ << " \n"; + std::cout << TETHERINGFREQ_STR << ": " << tethering_freq_ << " \n"; std::cout << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"; } diff --git a/src/dbus/gdbus.cpp b/src/dbus/gdbus.cpp index 80e16e4..a4fb224 100644 --- a/src/dbus/gdbus.cpp +++ b/src/dbus/gdbus.cpp @@ -10,14 +10,14 @@ namespace Amarula::DBus::G { -void DBus::on_any_async_done() { +void DBus::onAnyAsyncDone() { std::lock_guard const lock(mtx_); if (pending_calls_-- == 1 && !running_ && loop_ != nullptr) { g_main_loop_quit(loop_); } } -void DBus::on_any_async_start() { ++pending_calls_; } +void DBus::onAnyAsyncStart() { ++pending_calls_; } DBus::DBus(const std::string& bus_name, const std::string& object_path) { GError* error = nullptr; diff --git a/src/dbus/gdbus_private.hpp b/src/dbus/gdbus_private.hpp index 4a66d95..eab9136 100644 --- a/src/dbus/gdbus_private.hpp +++ b/src/dbus/gdbus_private.hpp @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -15,9 +16,11 @@ class EnumStringMap { public: using Pair = std::pair; - constexpr explicit EnumStringMap(std::array init) : data_{init} {} + constexpr explicit EnumStringMap(const std::array& init) + : data_{init} {} - [[nodiscard]] constexpr std::string_view to_string(Enum enum_value) const { + [[nodiscard]] constexpr auto toString(Enum enum_value) const + -> std::string_view { for (auto&& [e, s] : data_) { if (e == enum_value) { return s; @@ -26,7 +29,8 @@ class EnumStringMap { throw std::runtime_error("Invalid enum value"); } - [[nodiscard]] constexpr Enum from_string(std::string_view str) const { + [[nodiscard]] constexpr auto fromString(std::string_view str) const + -> Enum { for (auto&& [e, s] : data_) { if (s == str) { return e; @@ -40,17 +44,17 @@ class EnumStringMap { }; template -auto as_to_vector(GVariant* array_s, const EnumStringMap* enumMap = +auto as_to_vector(GVariant* array_s, const EnumStringMap* enum_map = nullptr) -> std::vector { - GVariantIter strIter; + GVariantIter str_iter; std::vector container; if (g_variant_is_of_type(array_s, G_VARIANT_TYPE("as")) != 0U) { - g_variant_iter_init(&strIter, array_s); + g_variant_iter_init(&str_iter, array_s); GVariant* item = nullptr; - while ((item = g_variant_iter_next_value(&strIter)) != nullptr) { + while ((item = g_variant_iter_next_value(&str_iter)) != nullptr) { const gchar* item_str = g_variant_get_string(item, nullptr); if constexpr (std::is_enum_v) { - container.emplace_back(enumMap->from_string(item_str)); + container.emplace_back(enum_map->fromString(item_str)); } else { container.emplace_back(item_str); } @@ -60,4 +64,20 @@ auto as_to_vector(GVariant* array_s, const EnumStringMap* enumMap = return container; } +using VariantPtr = std::unique_ptr; + +static inline auto vector_to_as(const std::vector& vec) + -> VariantPtr { + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE("as")); + + for (const auto& string : vec) { + g_variant_builder_add_value(&builder, + g_variant_new_string(string.c_str())); + } + + GVariant* var = g_variant_builder_end(&builder); + return VariantPtr{g_variant_ref_sink(var), &g_variant_unref}; +} + } // namespace Amarula::DBus::G diff --git a/tests/gconnman_serv_test.cpp b/tests/gconnman_serv_test.cpp index 197315b..ff54298 100644 --- a/tests/gconnman_serv_test.cpp +++ b/tests/gconnman_serv_test.cpp @@ -16,7 +16,7 @@ using Type = Amarula::DBus::G::Connman::TechProperties::Type; TEST(Connman, getServs) { bool called = false; { - Connman connman; + const Connman connman; const auto manager = connman.manager(); manager->onTechnologiesChanged([&](const auto& technologies) { ASSERT_FALSE(technologies.empty()) << "No technologies returned"; @@ -53,11 +53,39 @@ TEST(Connman, getServs) { ASSERT_TRUE(called) << "TechnologiesChanged callback was never called"; } +TEST(Connman, setNameServers) { + bool called = false; + { + const Connman connman; + const auto manager = connman.manager(); + + manager->onServicesChanged([&](const auto& services) { + called = true; + ASSERT_FALSE(services.empty()); + for (const auto& serv : services) { + const auto props = serv->properties(); + const auto name = props.getName(); + props.print(); + serv->onPropertyChanged([](const auto& properties) { + std::cout << "onPropertyChange:\n"; + properties.print(); + }); + serv->setNameServers( + {"8.8.8.8", "4.4.4.4"}, [&, name](auto success) { + EXPECT_TRUE(success) << "Set setNameServers for " + << name << " did not succeed"; + }); + } + }); + } + ASSERT_TRUE(called) << "TechnologiesChanged callback was never called"; +} + TEST(Connman, ForgetAndDisconnectService) { bool called = false; { - Connman connman; + const Connman connman; const auto manager = connman.manager(); manager->onServicesChanged([&](const auto& services) { @@ -95,7 +123,7 @@ TEST(Connman, ConnectWifi) { bool called = false; bool called_request_input = false; { - Connman connman; + const Connman connman; const auto manager = connman.manager(); manager->onRequestInputPassphrase([&](auto service) -> auto { @@ -115,17 +143,35 @@ TEST(Connman, ConnectWifi) { const auto props = serv->properties(); std::cout << "Service: " << props.getName() << "\n"; const auto name = props.getName(); + const auto state = props.getState(); if (name == "connmantest") { std::cout << "Test wifi found\n"; - std::cout << "Connecting to service: " << name << '\n'; - serv->connect([serv](bool success) { - EXPECT_TRUE(success); - std::cout - << "Service connected successfully: " << success - << '\n'; - serv->properties().print(); - }); + + if (state == State::Idle) { + std::cout << "Connecting to service: " << name << '\n'; + props.print(); + serv->onPropertyChanged([](const auto& properties) { + std::cout << "onPropertyChange:\n"; + properties.print(); + }); + manager->registerAgent( + manager->internalAgentPath(), + [serv, manager](const auto success) { + EXPECT_TRUE(success); + serv->connect([serv, manager](bool success) { + EXPECT_TRUE(success); + std::cout + << "Service connected successfully: " + << success << '\n'; + serv->properties().print(); + manager->unregisterAgent( + manager->internalAgentPath()); + }); + }); + } + + break; } } });