From dbef053249c4bb1d48b98dc681a13885d5e93c8b Mon Sep 17 00:00:00 2001 From: Eduardo Gonzalez Date: Tue, 12 May 2026 13:02:42 +0200 Subject: [PATCH 1/5] gconnman_service.cpp: Add connman misspelled word MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Connman could set the IPv6·privacy·option as prefered and also preferred. Add the misspelled property value to the map for the same key. Signed-off-by: Eduardo Gonzalez --- src/dbus/gconnman_service.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dbus/gconnman_service.cpp b/src/dbus/gconnman_service.cpp index f5fbcd3..0a9d74d 100644 --- a/src/dbus/gconnman_service.cpp +++ b/src/dbus/gconnman_service.cpp @@ -79,10 +79,11 @@ static constexpr EnumStringMap IPV6_METHOD_MAP{ {IPv6::Method::Fixed, "fixed"}, {IPv6::Method::Auto, "auto"}}}}; -static constexpr EnumStringMap IPV6_PRIVACY_MAP{ +static constexpr EnumStringMap IPV6_PRIVACY_MAP{ {{{IPv6::Privacy::Disabled, "disabled"}, {IPv6::Privacy::Enabled, "enabled"}, - {IPv6::Privacy::Preferred, "preferred"}}}}; + {IPv6::Privacy::Preferred, "preferred"}, + {IPv6::Privacy::Preferred, "prefered"}}}}; Service::Service(DBus* dbus, const gchar* obj_path) : DBusProxy(dbus, SERVICE, obj_path, SERVICE_INTERFACE) {} From e0973c638a8a9b14f5132c421e20a90985f62a1c Mon Sep 17 00:00:00 2001 From: Eduardo Gonzalez Date: Fri, 8 May 2026 12:14:39 +0200 Subject: [PATCH 2/5] Remove use of cout and print Remove the print method of the different properties. The consumer should define the output stream to put the properties. Remove unused iostream header includes. Signed-off-by: Eduardo Gonzalez --- examples/connmanctl.cpp | 4 +- include/amarula/dbus/connman/gclock.hpp | 3 +- include/amarula/dbus/connman/gservice.hpp | 18 +- include/amarula/dbus/connman/gtechnology.hpp | 3 +- include/amarula/dbus/gproxy.hpp | 1 - src/dbus/gconnman_agent.cpp | 1 - src/dbus/gconnman_clock.cpp | 38 ++-- src/dbus/gconnman_service.cpp | 179 ++++++++++--------- src/dbus/gconnman_technology.cpp | 18 +- tests/gconnman_clock_test.cpp | 20 +-- tests/gconnman_serv_test.cpp | 18 +- tests/gconnman_tech_test.cpp | 8 +- 12 files changed, 158 insertions(+), 153 deletions(-) diff --git a/examples/connmanctl.cpp b/examples/connmanctl.cpp index 74b1965..60beabe 100644 --- a/examples/connmanctl.cpp +++ b/examples/connmanctl.cpp @@ -75,7 +75,7 @@ auto main() -> int { const auto props = tech->properties(); std::cout << "Technology: " << props.getName() << " " << tech->objPath() << "\n"; - props.print(); + std::cout << props; } } } @@ -128,7 +128,7 @@ auto main() -> int { const auto props = (*iterator)->properties(); std::cout << "Service: " << props.getName() << " " << (*iterator)->objPath() << "\n"; - props.print(); + std::cout << props; } else { std::cout << "Service not found: " << arg << "\n"; } diff --git a/include/amarula/dbus/connman/gclock.hpp b/include/amarula/dbus/connman/gclock.hpp index 18d005d..1c8787f 100644 --- a/include/amarula/dbus/connman/gclock.hpp +++ b/include/amarula/dbus/connman/gclock.hpp @@ -25,7 +25,8 @@ struct ClockProperties { return time_server_synced_; } - void print() const; + friend auto operator<<(std::ostream& ostr, + const ClockProperties& object) -> std::ostream&; private: uint64_t time_{0}; diff --git a/include/amarula/dbus/connman/gservice.hpp b/include/amarula/dbus/connman/gservice.hpp index 56b696e..dd4d9e9 100644 --- a/include/amarula/dbus/connman/gservice.hpp +++ b/include/amarula/dbus/connman/gservice.hpp @@ -37,7 +37,8 @@ class IPv4 : public GVariantParser { Auto, }; - void print() const; + friend auto operator<<(std::ostream& ostr, + const IPv4& object) -> std::ostream&; [[nodiscard]] auto getMethod() const { return method_; } [[nodiscard]] auto getAddress() const { return address_; } @@ -65,7 +66,8 @@ struct IPv6 : public GVariantParser { Auto, }; enum class Privacy : uint8_t { Disabled = 0, Enabled, Preferred }; - void print() const; + friend auto operator<<(std::ostream& ostr, + const IPv6& object) -> std::ostream&; [[nodiscard]] auto getMethod() const { return method_; } [[nodiscard]] auto getAddress() const { return address_; } [[nodiscard]] auto getGateway() const { return gateway_; } @@ -87,7 +89,8 @@ struct IPv6 : public GVariantParser { struct Ethernet : public GVariantParser { public: enum class Method : uint8_t { Manual = 0, Auto }; - void print() const; + friend auto operator<<(std::ostream& ostr, + const Ethernet& object) -> std::ostream&; [[nodiscard]] auto getMethod() const { return method_; } [[nodiscard]] auto getInterface() const { return interface_; } [[nodiscard]] auto getAddress() const { return address_; } @@ -106,7 +109,8 @@ struct Ethernet : public GVariantParser { struct Provider : public GVariantParser { public: - void print() const; + friend auto operator<<(std::ostream& ostr, + const Provider& object) -> std::ostream&; [[nodiscard]] auto getHost() const { return host_; } [[nodiscard]] auto getDomain() const { return domain_; } [[nodiscard]] auto getName() const { return name_; } @@ -126,7 +130,8 @@ struct Provider : public GVariantParser { struct Proxy : public GVariantParser { public: enum class Method : uint8_t { Direct = 0, Auto, Manual }; - void print() const; + friend auto operator<<(std::ostream& ostr, + const Proxy& object) -> std::ostream&; [[nodiscard]] auto getMethod() const { return method_; } [[nodiscard]] auto getUrl() const { return url_; } [[nodiscard]] auto getServers() const { return servers_; } @@ -186,7 +191,8 @@ struct ServProperties { OnlineCheckFailed }; - void print() const; + friend auto operator<<(std::ostream& ostr, + const ServProperties& object) -> std::ostream&; [[nodiscard]] auto getState() const { return state_; } [[nodiscard]] auto getType() const { return type_; } [[nodiscard]] auto getSecurity() const { return security_; } diff --git a/include/amarula/dbus/connman/gtechnology.hpp b/include/amarula/dbus/connman/gtechnology.hpp index c11e24f..53010a2 100644 --- a/include/amarula/dbus/connman/gtechnology.hpp +++ b/include/amarula/dbus/connman/gtechnology.hpp @@ -29,7 +29,8 @@ struct TechProperties { [[nodiscard]] auto isConnected() const { return connected_; } [[nodiscard]] auto isTethering() const { return tethering_; } [[nodiscard]] auto getTetheringFreq() const { return tethering_freq_; } - void print() const; + friend auto operator<<(std::ostream& ostr, + const TechProperties& object) -> std::ostream&; private: bool powered_ = false; diff --git a/include/amarula/dbus/gproxy.hpp b/include/amarula/dbus/gproxy.hpp index 57ef01e..95940c5 100644 --- a/include/amarula/dbus/gproxy.hpp +++ b/include/amarula/dbus/gproxy.hpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/src/dbus/gconnman_agent.cpp b/src/dbus/gconnman_agent.cpp index e032070..7d36400 100644 --- a/src/dbus/gconnman_agent.cpp +++ b/src/dbus/gconnman_agent.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include diff --git a/src/dbus/gconnman_clock.cpp b/src/dbus/gconnman_clock.cpp index 2cc45a9..45ee737 100644 --- a/src/dbus/gconnman_clock.cpp +++ b/src/dbus/gconnman_clock.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -97,26 +96,25 @@ void Clock::setTimeServers(const std::vector& servers, g_variant_builder_clear(&builder); } -void ClockProperties::print() const { - LCM_LOG("@@@@@@@@@@ ClockProperties: @@@@@@@@@@@@@@@\n"); - LCM_LOG(TIME_STR << ": " << time_ << " ("); - const auto time_value = static_cast(time_); - LCM_LOG(std::put_time(std::localtime(&time_value), "%Y-%m-%d %H:%M:%S") - << ")\n"); - LCM_LOG(TIMEUPDATES_STR << ": " << TIME_UPDATE_MAP.toString(time_updates_) - << '\n'); - LCM_LOG(TIMEZONE_STR << ": " << timezone_ << '\n'); - LCM_LOG(TIMEZONEUPDATES_STR - << ": " << TIME_ZONE_UPDATE_MAP.toString(timezone_updates_) - << '\n'); - LCM_LOG(TIMESERVERSYNCED_STR << ": " << std::boolalpha - << time_server_synced_ << '\n'); - LCM_LOG(TIMESERVERS_STR << ": "); - for (const auto& server : time_servers_) { - LCM_LOG(server << ' '); +auto operator<<(std::ostream& ost, + const ClockProperties& obj) -> std::ostream& { + ost << TIME_STR << ": " << obj.time_ << " ("; + const auto time_value = static_cast(obj.time_); + ost << std::put_time(std::localtime(&time_value), "%Y-%m-%d %H:%M:%S") + << ")\n"; + ost << TIMEUPDATES_STR << ": " + << TIME_UPDATE_MAP.toString(obj.time_updates_) << '\n'; + ost << TIMEZONE_STR << ": " << obj.timezone_ << '\n'; + ost << TIMEZONEUPDATES_STR << ": " + << TIME_ZONE_UPDATE_MAP.toString(obj.timezone_updates_) << '\n'; + ost << TIMESERVERSYNCED_STR << ": " << std::boolalpha + << obj.time_server_synced_ << '\n'; + ost << TIMESERVERS_STR << ": "; + for (const auto& server : obj.time_servers_) { + ost << server << ' '; } - LCM_LOG('\n'); - LCM_LOG("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"); + ost << '\n'; + return ost; } } // namespace Amarula::DBus::G::Connman diff --git a/src/dbus/gconnman_service.cpp b/src/dbus/gconnman_service.cpp index 0a9d74d..2da369b 100644 --- a/src/dbus/gconnman_service.cpp +++ b/src/dbus/gconnman_service.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -288,132 +287,136 @@ void ServProperties::update(const gchar* key, GVariant* value) { } } -void ServProperties::print() const { - LCM_LOG("@@@@@@@@@@ ServProperties: @@@@@@@@@@@@@@@\n"); - LCM_LOG("State: " << STATE_MAP.toString(state_) << '\n'); - if (error_ != Error::None) { - LCM_LOG("Error: " << ERROR_MAP.toString(error_) << '\n'); +auto operator<<(std::ostream& ost, const ServProperties& obj) -> std::ostream& { + ost << "State: " << STATE_MAP.toString(obj.state_) << '\n'; + if (obj.error_ != Error::None) { + ost << "Error: " << ERROR_MAP.toString(obj.error_) << '\n'; } - LCM_LOG("Name: " << name_ << '\n'); - LCM_LOG("Type: " << TYPE_MAP.toString(type_) << '\n'); - LCM_LOG("Strength: " << static_cast(strength_) << '\n'); - LCM_LOG("AutoConnect: " << std::boolalpha << autoconnect_ << '\n'); - LCM_LOG("mDNS: " << mdns_ << '\n'); - LCM_LOG("Favorite: " << favorite_ << '\n'); - LCM_LOG("Immutable: " << immutable_ << '\n'); - LCM_LOG("Roaming: " << roaming_ << '\n'); - - if (security_) { - LCM_LOG("Security: "); - for (const auto& sec : security_.value()) { - LCM_LOG(SECURITY_MAP.toString(sec) << ' '); + ost << "Name: " << obj.name_ << '\n'; + ost << "Type: " << TYPE_MAP.toString(obj.type_) << '\n'; + ost << "Strength: " << static_cast(obj.strength_) << '\n'; + ost << "AutoConnect: " << std::boolalpha << obj.autoconnect_ << '\n'; + ost << "mDNS: " << obj.mdns_ << '\n'; + ost << "Favorite: " << obj.favorite_ << '\n'; + ost << "Immutable: " << obj.immutable_ << '\n'; + ost << "Roaming: " << obj.roaming_ << '\n'; + + if (obj.security_) { + ost << "Security: "; + for (const auto& sec : obj.security_.value()) { + ost << SECURITY_MAP.toString(sec) << ' '; } - LCM_LOG('\n'); + ost << '\n'; } - if (name_servers_) { - LCM_LOG("Nameservers: "); - for (const auto& nserver : name_servers_.value()) { - LCM_LOG(nserver << ' '); + if (obj.name_servers_) { + ost << "Nameservers: "; + for (const auto& nserver : obj.name_servers_.value()) { + ost << nserver << ' '; } - LCM_LOG('\n'); + ost << '\n'; } - if (name_servers_conf_) { - LCM_LOG("Nameservers.Configuration: "); - for (const auto& nserver : name_servers_conf_.value()) { - LCM_LOG(nserver << ' '); + if (obj.name_servers_conf_) { + ost << "Nameservers.Configuration: "; + for (const auto& nserver : obj.name_servers_conf_.value()) { + ost << nserver << ' '; } - LCM_LOG('\n'); + ost << '\n'; } - if (domains_) { - LCM_LOG("Domains: "); - for (const auto& domain : domains_.value()) { - LCM_LOG(domain << ' '); + if (obj.domains_) { + ost << "Domains: "; + for (const auto& domain : obj.domains_.value()) { + ost << domain << ' '; } - LCM_LOG('\n'); + ost << '\n'; } - if (time_servers_) { - LCM_LOG("TimeServers: "); - for (const auto& tserver : time_servers_.value()) { - LCM_LOG(tserver << ' '); + if (obj.time_servers_) { + ost << "TimeServers: "; + for (const auto& tserver : obj.time_servers_.value()) { + ost << tserver << ' '; } - LCM_LOG('\n'); + ost << '\n'; } - if (ipv4_) { - ipv4_.value().print(); + if (obj.ipv4_) { + ost << obj.ipv4_.value(); } - if (ipv6_) { - ipv6_.value().print(); + if (obj.ipv6_) { + ost << obj.ipv6_.value(); } - if (ethernet_) { - ethernet_.value().print(); + if (obj.ethernet_) { + ost << obj.ethernet_.value(); } - if (provider_) { - provider_.value().print(); + if (obj.provider_) { + ost << obj.provider_.value(); } - if (proxy_) { - proxy_.value().print(); + if (obj.proxy_) { + ost << obj.proxy_.value(); } - LCM_LOG("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"); + return ost; } -void Ethernet::print() const { - LCM_LOG("Ethernet:\n"); - LCM_LOG(" Method: " << ETHERNET_METHOD_MAP.toString(method_) << '\n'); - LCM_LOG(" Interface: " << interface_ << '\n'); - LCM_LOG(" Address: " << address_ << '\n'); - LCM_LOG(" MTU: " << mtu_ << '\n'); +auto operator<<(std::ostream& ost, const Ethernet& obj) -> std::ostream& { + ost << "Ethernet:\n"; + ost << " Method: " << ETHERNET_METHOD_MAP.toString(obj.method_) << '\n'; + ost << " Interface: " << obj.interface_ << '\n'; + ost << " Address: " << obj.address_ << '\n'; + ost << " MTU: " << obj.mtu_ << '\n'; + return ost; } -void IPv4::print() const { - LCM_LOG("IPv4:\n"); - LCM_LOG(" Method: " << IPV4_METHOD_MAP.toString(method_) << '\n'); - LCM_LOG(" Address: " << address_ << '\n'); - LCM_LOG(" Netmask: " << netmask_ << '\n'); - LCM_LOG(" Gateway: " << gateway_ << '\n'); +auto operator<<(std::ostream& ost, const IPv4& obj) -> std::ostream& { + ost << "IPv4:\n"; + ost << " Method: " << IPV4_METHOD_MAP.toString(obj.method_) << '\n'; + ost << " Address: " << obj.address_ << '\n'; + ost << " Netmask: " << obj.netmask_ << '\n'; + ost << " Gateway: " << obj.gateway_ << '\n'; + return ost; } -void Provider::print() const { - LCM_LOG("Provider:\n"); - LCM_LOG(" Host: " << host_ << '\n'); - LCM_LOG(" Domain: " << domain_ << '\n'); - LCM_LOG(" Name: " << name_ << '\n'); - LCM_LOG(" Type: " << type_ << '\n'); +auto operator<<(std::ostream& ost, const Provider& obj) -> std::ostream& { + ost << "Provider:\n"; + ost << " Host: " << obj.host_ << '\n'; + ost << " Domain: " << obj.domain_ << '\n'; + ost << " Name: " << obj.name_ << '\n'; + ost << " Type: " << obj.type_ << '\n'; + return ost; } -void IPv6::print() const { - LCM_LOG("IPv6:\n"); - LCM_LOG(" Method: " << IPV6_METHOD_MAP.toString(method_) << '\n'); - LCM_LOG(" Address: " << address_ << '\n'); - LCM_LOG(" Gateway: " << gateway_ << '\n'); - LCM_LOG(" Privacy: " << static_cast(privacy_) << '\n'); - LCM_LOG(" Prefix Length: " << static_cast(prefix_length_) << '\n'); +auto operator<<(std::ostream& ost, const IPv6& obj) -> std::ostream& { + ost << "IPv6:\n"; + ost << " Method: " << IPV6_METHOD_MAP.toString(obj.method_) << '\n'; + ost << " Address: " << obj.address_ << '\n'; + ost << " Gateway: " << obj.gateway_ << '\n'; + ost << " Privacy: " << static_cast(obj.privacy_) << '\n'; + ost << " Prefix Length: " << static_cast(obj.prefix_length_) << '\n'; + return ost; } -void Proxy::print() const { - LCM_LOG("Proxy:\n"); - LCM_LOG(" Method: " << PROXY_METHOD_MAP.toString(method_) << '\n'); - LCM_LOG(" URL: " << url_ << '\n'); - LCM_LOG(" Servers: "); - for (const auto& server : servers_) { - LCM_LOG(server << ' '); +auto operator<<(std::ostream& ost, const Proxy& obj) -> std::ostream& { + ost << "Proxy:\n"; + ost << " Method: " << PROXY_METHOD_MAP.toString(obj.method_) << '\n'; + ost << " URL: " << obj.url_ << '\n'; + ost << " Servers: "; + for (const auto& server : obj.servers_) { + ost << server << ' '; } - LCM_LOG('\n'); + ost << '\n'; - LCM_LOG(" Excludes: "); - for (const auto& exclude : excludes_) { - LCM_LOG(exclude << ' '); + ost << " Excludes: "; + for (const auto& exclude : obj.excludes_) { + ost << exclude << ' '; } - LCM_LOG('\n'); + ost << '\n'; + return ost; } } // namespace Amarula::DBus::G::Connman diff --git a/src/dbus/gconnman_technology.cpp b/src/dbus/gconnman_technology.cpp index d94a80b..15359ef 100644 --- a/src/dbus/gconnman_technology.cpp +++ b/src/dbus/gconnman_technology.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include "gconnman_private.hpp" @@ -60,15 +59,14 @@ void TechProperties::update(const gchar* key, GVariant* value) { } } -void TechProperties::print() const { - LCM_LOG("@@@@@@@@@@ TechProperties: @@@@@@@@@@@@@@@\n"); - LCM_LOG(NAME_STR << ": " << name_ << '\n'); - LCM_LOG(TYPE_STR << ": " << TYPE_MAP.toString(type_) << '\n'); - LCM_LOG(POWERED_STR << ": " << std::boolalpha << powered_ << '\n'); - LCM_LOG(CONNECTED_STR << ": " << std::boolalpha << connected_ << '\n'); - LCM_LOG(TETHERING_STR << ": " << std::boolalpha << tethering_ << '\n'); - LCM_LOG(TETHERINGFREQ_STR << ": " << tethering_freq_ << " \n"); - LCM_LOG("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"); +auto operator<<(std::ostream& ost, const TechProperties& obj) -> std::ostream& { + ost << NAME_STR << ": " << obj.name_ << '\n'; + ost << TYPE_STR << ": " << TYPE_MAP.toString(obj.type_) << '\n'; + ost << POWERED_STR << ": " << std::boolalpha << obj.powered_ << '\n'; + ost << CONNECTED_STR << ": " << std::boolalpha << obj.connected_ << '\n'; + ost << TETHERING_STR << ": " << std::boolalpha << obj.tethering_ << '\n'; + ost << TETHERINGFREQ_STR << ": " << obj.tethering_freq_ << " \n"; + return ost; } } // namespace Amarula::DBus::G::Connman diff --git a/tests/gconnman_clock_test.cpp b/tests/gconnman_clock_test.cpp index c34ac0e..c76a6ef 100644 --- a/tests/gconnman_clock_test.cpp +++ b/tests/gconnman_clock_test.cpp @@ -27,7 +27,7 @@ TEST(Connman, ClockSetTimeUpdates) { EXPECT_NE(callback_tid, main_tid); EXPECT_NE(callback_tid, loop_tid); std::cout << "onPropertyChanged:\n"; - props.print(); + std::cout << props; }); connman.clock()->setTimeUpdates( TimeUpdate::Auto, [main_tid = thread_bundle.main_tid, @@ -45,7 +45,7 @@ TEST(Connman, ClockSetTime) { const guint time = TEST_TIME; connman.clock()->onPropertyChanged([](auto& props) { std::cout << "onPropertyChanged:\n"; - props.print(); + std::cout << props; }); connman.clock()->setTimeUpdates( TimeUpdate::Manual, [&connman](auto success) { @@ -54,7 +54,7 @@ TEST(Connman, ClockSetTime) { EXPECT_TRUE(success); connman.clock()->getProperties([](auto& props) { std::cout << "getProperties:\n"; - props.print(); + std::cout << props; }); }); }); @@ -65,7 +65,7 @@ TEST(Connman, ClockSetTimeServers1) { const Connman connman; connman.clock()->onPropertyChanged([](auto& props) { std::cout << "onPropertyChanged:\n"; - props.print(); + std::cout << props; }); const std::vector servers = {"time1.google.com", "time2.google.com"}; @@ -78,7 +78,7 @@ TEST(Connman, ClockSetTimeServers2) { const Connman connman; connman.clock()->onPropertyChanged([](auto& props) { std::cout << "onPropertyChanged:\n"; - props.print(); + std::cout << props; }); const std::vector servers = {"time1.example.com", "time2.example.com"}; @@ -86,7 +86,7 @@ TEST(Connman, ClockSetTimeServers2) { EXPECT_TRUE(success); connman.clock()->getProperties([](auto& props) { std::cout << "getProperties:\n"; - props.print(); + std::cout << props; }); }); } @@ -96,7 +96,7 @@ TEST(Connman, ClockSetTimeZoneUpdates) { const Connman connman; connman.clock()->onPropertyChanged([](auto& props) { std::cout << "onPropertyChanged:\n"; - props.print(); + std::cout << props; }); connman.clock()->setTimeZoneUpdates( TimeZoneUpdate::Auto, @@ -108,7 +108,7 @@ TEST(Connman, ClockSetTimeZone) { const Connman connman; connman.clock()->onPropertyChanged([](auto& props) { std::cout << "onPropertyChanged:\n"; - props.print(); + std::cout << props; }); connman.clock()->setTimeZoneUpdates( TimeZoneUpdate::Manual, [&connman](auto success) { @@ -118,7 +118,7 @@ TEST(Connman, ClockSetTimeZone) { EXPECT_TRUE(success); connman.clock()->getProperties([](auto& props) { std::cout << "getProperties:\n"; - props.print(); + std::cout << props; }); }); }); @@ -145,6 +145,6 @@ TEST(Connman, ClockGetProperties) { const auto callback_tid = std::this_thread::get_id(); EXPECT_NE(callback_tid, main_tid); EXPECT_NE(callback_tid, loop_tid); - props.print(); + std::cout << props; }); } diff --git a/tests/gconnman_serv_test.cpp b/tests/gconnman_serv_test.cpp index 580e1eb..0f9e5e4 100644 --- a/tests/gconnman_serv_test.cpp +++ b/tests/gconnman_serv_test.cpp @@ -40,7 +40,7 @@ TEST(Connman, getServs) { << "Technology " << prop.getName() << " was not powered ON"; std::cout << "onPropertyChanged:\n"; - prop.print(); + std::cout << prop; }); const auto prop = tech->properties(); const auto name = prop.getName(); @@ -67,7 +67,7 @@ TEST(Connman, getServs) { ASSERT_FALSE(services.empty()); for (const auto& serv : services) { const auto props = serv->properties(); - props.print(); + std::cout << props; } }); } @@ -92,10 +92,10 @@ TEST(Connman, setNameServers) { for (const auto& serv : services) { const auto props = serv->properties(); const auto name = props.getName(); - props.print(); + std::cout << props; serv->onPropertyChanged([](const auto& properties) { std::cout << "onPropertyChange:\n"; - properties.print(); + std::cout << properties; }); serv->setNameServers( {"8.8.8.8", "4.4.4.4"}, @@ -143,7 +143,7 @@ TEST(Connman, ForgetAndDisconnectService) { EXPECT_NE(callback_tid, loop_tid); EXPECT_TRUE(success); std::cout << "Service removed: " << name << '\n'; - serv->properties().print(); + std::cout << serv->properties(); }); } else if (state == State::Ready || state == State::Online) { std::cout << "Disconnecting service: " << name << '\n'; @@ -154,7 +154,7 @@ TEST(Connman, ForgetAndDisconnectService) { EXPECT_TRUE(success); std::cout << "Service disconnected: " << serv->objPath() << '\n'; - serv->properties().print(); + std::cout << serv->properties(); }); } } @@ -207,7 +207,7 @@ TEST(Connman, ConnectWifi) { if (state == State::Idle) { std::cout << "Connecting to service: " << name << '\n'; - props.print(); + std::cout << props; serv->onPropertyChanged( [main_tid, loop_tid](const auto& properties) { const auto callback_tid = @@ -215,7 +215,7 @@ TEST(Connman, ConnectWifi) { EXPECT_NE(callback_tid, main_tid); EXPECT_NE(callback_tid, loop_tid); std::cout << "onPropertyChange:\n"; - properties.print(); + std::cout << properties; }); manager->registerAgent( manager->internalAgentPath(), @@ -236,7 +236,7 @@ TEST(Connman, ConnectWifi) { std::cout << "Service connected successfully: " << success << '\n'; - serv->properties().print(); + std::cout << serv->properties(); manager->unregisterAgent( manager->internalAgentPath()); }); diff --git a/tests/gconnman_tech_test.cpp b/tests/gconnman_tech_test.cpp index 8c9ba7a..419e486 100644 --- a/tests/gconnman_tech_test.cpp +++ b/tests/gconnman_tech_test.cpp @@ -30,7 +30,7 @@ TEST(Connman, getTechs) { EXPECT_TRUE(props.isPowered()) << "Technology is connected but not powered"; } - props.print(); + std::cout << props; } }); } @@ -63,7 +63,7 @@ TEST(Connman, PowerOnAllTechnologies) { EXPECT_NE(callback_tid, main_tid); EXPECT_NE(callback_tid, loop_tid); std::cout << "onPropertyChanged:\n"; - prop.print(); + std::cout << prop; }); const auto prop = tech->properties(); const auto name = prop.getName(); @@ -141,7 +141,7 @@ TEST(Connman, PowerOffAllTechnologies) { << "Technology " << prop.getName() << " was not powered OFF"; std::cout << "onPropertyChanged:\n"; - prop.print(); + std::cout << prop; }); const auto prop = tech->properties(); const auto name = prop.getName(); @@ -160,4 +160,4 @@ TEST(Connman, PowerOffAllTechnologies) { }); } ASSERT_TRUE(called) << "setPowered callback was never called"; -} \ No newline at end of file +} From 42e85c18854697b0348c324ae1d037b6009c6156 Mon Sep 17 00:00:00 2001 From: Eduardo Gonzalez Date: Fri, 8 May 2026 12:25:12 +0200 Subject: [PATCH 3/5] connmanctl.cpp: Fix logic jump Signed-off-by: Eduardo Gonzalez --- examples/connmanctl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/connmanctl.cpp b/examples/connmanctl.cpp index 60beabe..d85fbaa 100644 --- a/examples/connmanctl.cpp +++ b/examples/connmanctl.cpp @@ -79,7 +79,7 @@ auto main() -> int { } } } - if (cmd == "agent") { + else if (cmd == "agent") { auto on_register = [arg](const auto success) { if (success) { std::cout << "Agent " From df5c263af1a18d6d6d731007b8434e6c377915c9 Mon Sep 17 00:00:00 2001 From: Eduardo Gonzalez Date: Tue, 5 May 2026 16:04:47 +0200 Subject: [PATCH 4/5] Add readline in connmanctl example Add readline in the connmanctl_dbus example. Allows completion, line editing, and interactive history while using the app. Improve command string manipulation and align better on how original connmanctl works. Signed-off-by: Eduardo Gonzalez --- examples/CMakeLists.txt | 2 + examples/connmanctl.cpp | 464 +++++++++++++++++++++++++++++----------- 2 files changed, 339 insertions(+), 127 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index c0a2070..cbba90c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,6 +1,8 @@ if(BUILD_CONNMAN) add_executable(connmanctl_dbus connmanctl.cpp) target_link_libraries(connmanctl_dbus PRIVATE GConnmanDbus) + pkg_check_modules(READLINE REQUIRED IMPORTED_TARGET readline) + target_link_libraries(connmanctl_dbus PRIVATE PkgConfig::READLINE) install( TARGETS connmanctl_dbus diff --git a/examples/connmanctl.cpp b/examples/connmanctl.cpp index d85fbaa..64cac59 100644 --- a/examples/connmanctl.cpp +++ b/examples/connmanctl.cpp @@ -1,3 +1,10 @@ +// clang-format off +#include +#include +#include +#include +// clang-format on + #include #include #include @@ -13,39 +20,236 @@ using Amarula::DBus::G::Connman::Connman; using Amarula::DBus::G::Connman::TechProperties; using State = Amarula::DBus::G::Connman::ServProperties::State; +using TechnologyType = TechProperties::Type; + +std::atomic has_message{false}; +std::ostringstream message; +std::mutex message_mutex; +std::mutex hash_mutex; +std::mutex cin_mutex; +std::condition_variable cin_cv; + +std::string passphrase; +std::string connecting_name; +std::atomic connecting = false; + +auto event_hook() -> int { + if (has_message.exchange(false)) { + rl_clear_visible_line(); + { + std::lock_guard lock(message_mutex); + std::cout << message.str(); + message.str(""); + message.clear(); + } + rl_on_new_line(); + rl_redisplay(); + } + + if (connecting) { + std::string prompt = "Enter passphrase for " + connecting_name + ": "; + + rl_set_prompt(prompt.c_str()); + rl_redisplay(); + } + return 0; +} + +enum class Command : std::uint8_t { + Technologies, + Services, + Scan, + Enable, + Disable, + Connect, + Disconnect, + Remove, + Agent, + Quit +}; + +inline const std::unordered_map command_map = { + {Command::Technologies, "technologies"}, + {Command::Services, "services"}, + {Command::Scan, "scan"}, + {Command::Enable, "enable"}, + {Command::Disable, "disable"}, + {Command::Connect, "connect"}, + {Command::Disconnect, "disconnect"}, + {Command::Remove, "remove"}, + {Command::Agent, "agent"}, + {Command::Quit, "quit"}}; + +inline const std::unordered_map tech_map = { + {TechnologyType::Ethernet, "ethernet"}, + {TechnologyType::Wifi, "wifi"}, + {TechnologyType::Cellular, "cellular"}, + {TechnologyType::Bluetooth, "bluetooth"}, + {TechnologyType::Vpn, "vpn"}, + {TechnologyType::Wired, "wired"}, + {TechnologyType::P2p, "p2p"}, + {TechnologyType::Gps, "gps"}, + {TechnologyType::Gadget, "gadget"}}; + +inline const std::vector commands_container = [] { + std::vector string_vector; + string_vector.reserve(command_map.size()); + + for (const auto& [cmd, name] : command_map) { + string_vector.emplace_back(name); + } + + return string_vector; +}(); + +inline const std::vector on_off_container{"on", "off"}; +inline const std::vector enable_disable_container{"enable", + "disable"}; + +inline std::vector services_container; +inline std::vector technologies_container; + +void printContainer(const std::vector& container) { + for (size_t i = 0; i < container.size(); ++i) { + std::cout << container[i]; + if (i + 1 < container.size()) { + std::cout << ", "; + } + } + std::cout << "\n"; +} + +enum class Mode : std::uint8_t { Command, ServiceHash, TechHash, OnOff }; + +inline Mode completion_mode = Mode::Command; + +auto command_generator(const char* text, int state) -> char* { + static size_t index; + static size_t len; + static std::vector variable_container; + + if (state == 0) { + index = 0; + len = strlen(text); + if (completion_mode == Mode::Command) { + variable_container = commands_container; + } else if (completion_mode == Mode::ServiceHash) { + std::lock_guard lock(hash_mutex); + variable_container = services_container; + } else if (completion_mode == Mode::TechHash) { + std::lock_guard lock(hash_mutex); + variable_container = technologies_container; + } else if (completion_mode == Mode::OnOff) { + variable_container = on_off_container; + } + } + + while (index < variable_container.size()) { + const auto cmd = variable_container[index++]; + + if (cmd.compare(0, len, text) == 0) { + return strdup(cmd.c_str()); + } + } + + return nullptr; +} + +auto completion(const char* text, int start, int end) -> char** { + (void)start; + (void)end; + if (start == 0) { + completion_mode = Mode::Command; + } else { + std::string line = rl_line_buffer; + std::istringstream iss(line); + std::string command; + std::string arg; + iss >> command >> arg; + if (command == command_map.at(Command::Agent)) { + completion_mode = Mode::OnOff; + } else if (command == command_map.at(Command::Enable) || + command == command_map.at(Command::Disable) || + command == command_map.at(Command::Scan)) { + completion_mode = Mode::TechHash; + } else if (command == command_map.at(Command::Connect) || + command == command_map.at(Command::Disconnect) || + command == command_map.at(Command::Remove) || + command == command_map.at(Command::Services)) { + completion_mode = Mode::ServiceHash; + } else { + return nullptr; + } + if (!arg.empty() && rl_point > 0 && rl_point == line.size() && + line[rl_point - 1] == ' ') { + return nullptr; + } + } + + return rl_completion_matches(text, command_generator); +} auto main() -> int { - std::mutex cin_mutex; - std::condition_variable cin_cv; - bool connecting = false; - Amarula::Log::enable(true); + rl_event_hook = event_hook; + rl_attempted_completion_function = completion; Connman connman; const auto manager = connman.manager(); manager->onRequestInputPassphrase([&](const auto& service) -> auto { const auto name = service->properties().getName(); - - std::string passphrase; - { - const std::unique_lock lock(cin_mutex); - std::cout << "Enter passphrase for " << name << " service\n"; - std::getline(std::cin, passphrase); - connecting = false; - } - cin_cv.notify_one(); + std::unique_lock lock(cin_mutex); + connecting = true; + connecting_name = name; + cin_cv.wait(lock, [] { return !connecting; }); return std::pair{true, passphrase}; }); - constexpr int CIN_WAIT_TIMEOUT_SECONDS = 30; + manager->onServicesChanged([](const auto& services) { + std::lock_guard lock(hash_mutex); + services_container.clear(); + for (const auto& serv : services) { + const auto path = serv->objPath(); + services_container.push_back( + path.substr(path.find_last_of('/') + 1)); + const auto props = serv->properties(); + const auto name = props.getName(); + if (!name.empty()) { + services_container.push_back(name); + } + } + }); + manager->onTechnologiesChanged([](const auto& technologies) { + std::lock_guard lock(hash_mutex); + technologies_container.clear(); + for (const auto& tech : technologies) { + const auto props = tech->properties(); + technologies_container.push_back( + tech_map.at(props.getType()).data()); + } + }); + std::string line; while (true) { - std::cout << "connmanctl> "; - { - std::unique_lock lock(cin_mutex); - cin_cv.wait_for(lock, - std::chrono::seconds(CIN_WAIT_TIMEOUT_SECONDS), - [&connecting] { return !connecting; }); - if (!std::getline(std::cin, line)) { - break; // EOF or error + char* raw_line = readline("connmanctl> "); + if (raw_line == nullptr) { + break; + } + + line = std::string(raw_line); + free(raw_line); + + if (connecting) { + { + std::lock_guard lock(cin_mutex); + passphrase = line; + connecting = false; + cin_cv.notify_all(); } + rl_set_prompt("connmanctl> "); + cin_cv.notify_all(); + continue; + } + + if (!line.empty()) { + add_history(line.c_str()); } // Trim whitespace line.erase(line.begin(), std::ranges::find_if(line, [](int character) { @@ -57,16 +261,23 @@ auto main() -> int { }) .base(), line.end()); - std::istringstream iss(line); std::string cmd; std::string arg; iss >> cmd >> arg; + auto match_service = [&arg](const auto& service) { + const auto props = service->properties(); + const auto name = props.getName(); + const auto path = service->objPath(); + const auto last = path.substr(path.find_last_of('/') + 1); + + return last == arg || name == arg; + }; - if (cmd == "exit" || cmd == "quit") { + if (cmd == command_map.at(Command::Quit)) { break; } - if (cmd == "technologies") { + if (cmd == command_map.at(Command::Technologies)) { const auto techs = manager->technologies(); if (techs.empty()) { std::cout << "No technologies available.\n"; @@ -78,14 +289,17 @@ auto main() -> int { std::cout << props; } } - } - else if (cmd == "agent") { + } else if (cmd == command_map.at(Command::Agent)) { auto on_register = [arg](const auto success) { if (success) { - std::cout << "Agent " - << ((arg == "on") ? "registered" : "unregistered") - << "\n"; + std::lock_guard lock(message_mutex); + message + << "Agent " + << ((arg == on_off_container.at(0)) ? "registered" + : "unregistered") + << "\n"; } + has_message = true; }; if (arg == "on") { manager->registerAgent(manager->internalAgentPath(), @@ -94,9 +308,10 @@ auto main() -> int { manager->unregisterAgent(manager->internalAgentPath(), on_register); } else { - std::cout << "Usage: agent on/off\n"; + std::cout << "Usage: agent " << on_off_container.at(0) << "/" + << on_off_container.at(1) << "\n"; } - } else if (cmd == "services") { + } else if (cmd == command_map.at(Command::Services)) { const auto services = manager->services(); if (services.empty()) { std::cout << "No services available.\n"; @@ -115,15 +330,14 @@ auto main() -> int { } else if (props.getState() == State::Ready) { std::cout << "R"; } + const auto path = service->objPath(); std::cout << " " << props.getName() << " " - << service->objPath() << "\n"; + << path.substr(path.find_last_of('/') + 1) + << "\n"; } } else { - auto iterator = std::ranges::find_if( - services, [&arg](const auto& service) { - return service->objPath() == arg || - service->properties().getName() == arg; - }); + auto iterator = + std::ranges::find_if(services, match_service); if (iterator != services.end()) { const auto props = (*iterator)->properties(); std::cout << "Service: " << props.getName() << " " @@ -134,82 +348,87 @@ auto main() -> int { } } } - } else if (cmd == "scan") { - if (arg.empty() || - (arg != "wifi" && arg != "ethernet" && arg != "bluetooth" && - arg != "cellular" && arg != "vpn")) { - std::cout << "Usage: scan \n"; - std::cout << "Available technologies: wifi, ethernet, " - "bluetooth, cellular, vpn\n"; - continue; + } else if (cmd == command_map.at(Command::Scan)) { + { + std::lock_guard lock(hash_mutex); + if (arg.empty() || + std::find(technologies_container.begin(), + technologies_container.end(), + arg) == technologies_container.end()) { + std::cout << "Usage: scan \n"; + std::cout << "Available technologies: "; + printContainer(technologies_container); + continue; + } } const auto techs = manager->technologies(); for (const auto& tech : techs) { const auto props = tech->properties(); const auto name = props.getName(); - if ((props.getType() == TechProperties::Type::Wifi && - arg == "wifi") || - (props.getType() == TechProperties::Type::Ethernet && - arg == "ethernet") || - (props.getType() == TechProperties::Type::Bluetooth && - arg == "bluetooth") || - (props.getType() == TechProperties::Type::Cellular && - arg == "cellular") || - (props.getType() == TechProperties::Type::Vpn && - arg == "vpn")) { + if (tech_map.at(props.getType()) == arg) { std::cout << "Scanning " << name << "...\n"; tech->scan([name](bool success) { - if (success) { - std::cout << "Technology " << name - << " scanned successfully.\n"; - } else { - std::cout << "Failed to scan technology " << name - << ".\n"; + { + std::lock_guard lock(message_mutex); + if (success) { + message << "Technology " << name + << " scanned successfully.\n"; + } else { + message << "Failed to scan technology " << name + << ".\n"; + } } + has_message = true; }); break; } } - } else if (cmd == "enable" || cmd == "disable") { - if (arg.empty() || - (arg != "wifi" && arg != "ethernet" && arg != "bluetooth" && - arg != "cellular" && arg != "vpn")) { - std::cout << "Usage: enable/disable \n"; - std::cout << "Available technologies: wifi, ethernet, " - "bluetooth, cellular, vpn\n"; - continue; + } else if (cmd == command_map.at(Command::Enable) || + cmd == command_map.at(Command::Disable)) { + { + std::lock_guard lock(hash_mutex); + if (arg.empty() || + std::find(technologies_container.begin(), + technologies_container.end(), + arg) == technologies_container.end()) { + std::cout << "Usage: " << enable_disable_container[0] << "/" + << enable_disable_container[1] + << " \n"; + std::cout << "Available technologies:"; + printContainer(technologies_container); + continue; + } } - const bool enable = (cmd == "enable"); + const bool enable = (cmd == enable_disable_container.at(0)); std::cout << (enable ? "Enabling" : "Disabling") << " technology: " << arg << "\n"; const auto techs = manager->technologies(); for (const auto& tech : techs) { const auto props = tech->properties(); const auto name = props.getName(); - if ((props.getType() == TechProperties::Type::Wifi && - arg == "wifi") || - (props.getType() == TechProperties::Type::Ethernet && - arg == "ethernet") || - (props.getType() == TechProperties::Type::Bluetooth && - arg == "bluetooth") || - (props.getType() == TechProperties::Type::Cellular && - arg == "cellular") || - (props.getType() == TechProperties::Type::Vpn && - arg == "vpn")) { - if ((!props.isPowered() && cmd == "enable") || - (props.isPowered() && cmd == "disable")) { + if (tech_map.at(props.getType()) == arg) { + if ((!props.isPowered() && + cmd == enable_disable_container.at(0)) || + (props.isPowered() && + cmd == enable_disable_container.at(1))) { std::cout << (enable ? "Enabling " : "Disabling ") << name << "...\n"; tech->setPowered(enable, [name, enable](bool success) { - if (success) { - std::cout << "Technology " << name - << (enable ? " enabled" : " disabled") - << " successfully.\n"; - } else { - std::cout << "Failed to " - << (enable ? " enable" : " disable") - << " technology " << name << "\n"; + { + std::lock_guard lock(message_mutex); + if (success) { + message + << "Technology " << name + << (enable ? " enabled" : " disabled") + << " successfully.\n"; + } else { + message << "Failed to " + << (enable ? " enable" : " disable") + << " technology " << name << "\n"; + } } + + has_message = true; }); } else { @@ -218,18 +437,15 @@ auto main() -> int { } } } - } else if (cmd == "connect" || cmd == "disconnect") { + } else if (cmd == command_map.at(Command::Connect) || + cmd == command_map.at(Command::Disconnect)) { if (arg.empty()) { std::cout << "Usage: connect/disconnect \n"; continue; } - const bool connect = (cmd == "connect"); + const bool connect = (cmd == command_map.at(Command::Connect)); const auto services = manager->services(); - auto iterator = - std::ranges::find_if(services, [&arg](const auto& service) { - return service->objPath() == arg || - service->properties().getName() == arg; - }); + auto iterator = std::ranges::find_if(services, match_service); if (iterator != services.end()) { const auto name = (*iterator)->properties().getName(); @@ -240,28 +456,23 @@ auto main() -> int { !connect) { std::cout << (connect ? "Connecting" : "Disconnecting") << " to service: " << name << "\n"; - const auto on_connect = [name, connect, &connecting, - &cin_mutex, - &cin_cv](bool success) { + const auto on_connect = [name, connect](bool success) { + std::lock_guard lock_message(message_mutex); if (success) { - std::cout + message << "Service " << name << (connect ? " connected" : " disconnected") << " successfully.\n"; } else { - std::cout << "Failed to " - << (connect ? " connect" : " disconnect") - << " to service " << name << "\n"; - } - { - const std::unique_lock lock(cin_mutex); - connecting = false; + message << "Failed to " + << (connect ? " connect" : " disconnect") + << " to service " << name << "\n"; } - cin_cv.notify_one(); + has_message = true; }; if (connect) { (*iterator)->connect(on_connect); - connecting = true; + } else { (*iterator)->disconnect(on_connect); } @@ -273,39 +484,38 @@ auto main() -> int { } else { std::cout << "Service " << arg << " not available.\n"; } - } else if (cmd == "remove") { + } else if (cmd == command_map.at(Command::Remove)) { if (arg.empty()) { std::cout << "Usage: remove \n"; continue; } const auto services = manager->services(); - auto iterator = - std::ranges::find_if(services, [&arg](const auto& service) { - return service->objPath() == arg || - service->properties().getName() == arg; - }); + auto iterator = std::ranges::find_if(services, match_service); if (iterator != services.end()) { const auto name = (*iterator)->properties().getName(); std::cout << "Removing service: " << name << "\n"; (*iterator)->remove([name](bool success) { - if (success) { - std::cout << "Service " << name - << " removed successfully.\n"; - } else { - std::cout << "Failed to remove service " << name - << "\n"; + { + std::lock_guard lock(message_mutex); + if (success) { + message << "Service " << name + << " removed successfully.\n"; + } else { + message << "Failed to remove service " << name + << "\n"; + } } + has_message = true; }); } else { std::cout << "Service " << arg << " not available.\n"; } } else { if (!cmd.empty()) { - std::cout - << "Unknown command: " << cmd - << ". Available commands: technologies, services, " - "scan, enable, disable, connect, disconnect, remove.\n"; + std::cout << "Unknown command: " << cmd + << ".\n Available commands: "; + printContainer(commands_container); } } } From 8b3b53867f146f94f982d3f0b4b8ee02b4f0767c Mon Sep 17 00:00:00 2001 From: Eduardo Gonzalez Date: Tue, 12 May 2026 13:52:04 +0200 Subject: [PATCH 5/5] conf-build-test.yml: Install readline dependency Signed-off-by: Eduardo Gonzalez --- .github/workflows/conf-build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/conf-build-test.yml b/.github/workflows/conf-build-test.yml index f42376c..fad795b 100644 --- a/.github/workflows/conf-build-test.yml +++ b/.github/workflows/conf-build-test.yml @@ -21,7 +21,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y cmake ninja-build doxygen clang-tidy python3-pip libglib2.0-dev + sudo apt-get install -y cmake ninja-build doxygen clang-tidy python3-pip libglib2.0-dev libreadline-dev - name: Check if formatted_code branch exists if: ${{ github.event_name == 'pull_request' && startsWith(github.base_ref, 'main') && github.head_ref == 'develop' }}