[RDK-E]: NetworkconnectionRecovery.sh migration to C/C++ Module#274
[RDK-E]: NetworkconnectionRecovery.sh migration to C/C++ Module#274udaykrishnag wants to merge 13 commits intodevelopfrom
Conversation
Signed-off-by: Balaji Punnuru <Balaji_Punnuru@comcast.com>
Signed-off-by: Balaji Punnuru <Balaji_Punnuru@comcast.com>
|
I have read the CLA Document and I hereby sign the CLA You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot. |
There was a problem hiding this comment.
Pull request overview
This PR introduces a new NetworkConnectionStats Thunder/WPEFramework module (in-process plugin + out-of-process implementation) intended to replace shell-based recovery/diagnostics with a C/C++ implementation that queries NetworkManager via COM-RPC/JSON-RPC and emits telemetry.
Changes:
- Adds an out-of-process diagnostics implementation that periodically generates network reports and subscribes to NetworkManager events.
- Adds two NetworkManager provider implementations (COM-RPC and JSON-RPC) plus a runtime factory selector.
- Integrates the new
networkstats/component into the build and adds configuration/docs assets.
Reviewed changes
Copilot reviewed 27 out of 28 changed files in this pull request and generated 24 comments.
Show a summary per file
| File | Description |
|---|---|
networkstats/plugin/ThunderJsonRPCProvider.h |
Declares JSON-RPC-based NetworkManager provider. |
networkstats/plugin/ThunderJsonRPCProvider.cpp |
Implements NetworkManager JSON-RPC calls (GetIPSettings, Ping, event subscribe). |
networkstats/plugin/ThunderComRPCProvider.h |
Declares COM-RPC-based NetworkManager provider. |
networkstats/plugin/ThunderComRPCProvider.cpp |
Implements NetworkManager COM-RPC calls (GetIPSettings, Ping, event subscribe). |
networkstats/plugin/NetworkDataProviderFactory.h |
Factory to select COM-RPC vs JSON-RPC provider based on config. |
networkstats/plugin/NetworkConnectionStatsLogger.h |
Adds a lightweight logging wrapper/macros for the module. |
networkstats/plugin/NetworkConnectionStatsLogger.cpp |
Implements stdout/RDK logger backend for logging wrapper. |
networkstats/plugin/NetworkConnectionStatsImplementation.h |
Declares out-of-process implementation class, threading, and event handlers. |
networkstats/plugin/NetworkConnectionStatsImplementation.cpp |
Implements periodic report generation, telemetry emission, and event-driven triggers. |
networkstats/plugin/NetworkConnectionStats.h |
Declares in-process plugin shell that spawns/aggregates the implementation. |
networkstats/plugin/NetworkConnectionStats.cpp |
Implements plugin lifecycle wiring and COM-RPC aggregation. |
networkstats/plugin/NetworkConnectionStats.config |
Adds example Thunder plugin JSON configuration. |
networkstats/plugin/NetworkConnectionStats.conf.in |
Adds config template for CMake write_config() generation. |
networkstats/plugin/Module.h |
Adds module header wiring for Thunder build macros/includes. |
networkstats/plugin/Module.cpp |
Adds module declaration for build reference. |
networkstats/plugin/INetworkData.h |
Defines common interface for network data providers. |
networkstats/plugin/CMakeLists.txt |
Builds the plugin + implementation libraries and installs config. |
networkstats/interface/INetworkConnectionStats.h |
Defines COM-RPC interface for the out-of-process implementation. |
networkstats/interface/CMakeLists.txt |
Generates ProxyStub library for the interface. |
networkstats/definition/NetworkConnectionStats.json |
Adds JSON-RPC API definition/documentation (currently not wired into build). |
networkstats/definition/CMakeLists.txt |
Adds JsonGenerator integration for docs/stubs generation. |
networkstats/THUNDER_PLUGIN_QUICK_REFERENCE.md |
Adds quick-reference documentation for the intended architecture. |
networkstats/THUNDER_PLUGIN_CONVERSION.md |
Adds conversion notes from standalone app to Thunder module. |
networkstats/README_INTERNAL_PLUGIN.md |
Describes internal-only plugin behavior/configuration and telemetry outputs. |
networkstats/NetworkStatsDesign_ver1.md |
Provides high-level design diagrams and operational flow. |
networkstats/CMakeLists.txt |
Adds networkstats sub-project build entrypoint. |
CMakeLists.txt |
Adds USE_TELEMETRY option and includes networkstats in the top-level build. |
.DS_Store |
Adds an unintended macOS metadata file. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #ifndef __THUNDERPROVIDER_H__ | ||
| #define __THUNDERPROVIDER_H__ |
There was a problem hiding this comment.
The include guard macro name starts with double underscores (THUNDERPROVIDER_H), which is reserved for the implementation in C/C++. Please rename it to a non-reserved identifier (e.g., THUNDER_JSONRPC_PROVIDER_H).
| #ifndef __THUNDERCOMRPCPROVIDER_H__ | ||
| #define __THUNDERCOMRPCPROVIDER_H__ |
There was a problem hiding this comment.
The include guard macro (THUNDERCOMRPCPROVIDER_H) begins with double underscores, which are reserved identifiers in C/C++. Please rename it to a non-reserved macro to avoid undefined behavior/toolchain issues.
| /* @brief Get DNS server entries */ | ||
| std::string NetworkJsonRPCProvider::getDnsEntries() | ||
| { | ||
| // TODO: Implement DNS entries retrieval | ||
| return ""; | ||
| } |
There was a problem hiding this comment.
getDnsEntries() is currently a stub that always returns an empty string. Since this provider can be selected at runtime via providerType=jsonrpc, this will lead to incorrect diagnostics when that provider is used; please implement DNS retrieval (or remove the method from the interface if it’s not needed).
| struct tm* lt; | ||
| const char* fileName = trimPath(file); | ||
|
|
||
| if (gDefaultLogLevel < level) | ||
| return; | ||
|
|
||
| gettimeofday(&tv, NULL); | ||
| lt = localtime(&tv.tv_sec); | ||
|
|
||
| printf("%.2d:%.2d:%.2d.%.6lld [%-5s] [PID=%d] [TID=%d] [%s +%d] %s : %s\n", lt->tm_hour, lt->tm_min, lt->tm_sec, (long long int)tv.tv_usec, levelMap[level], getpid(), gettid(), fileName, line, func, formattedLog); |
There was a problem hiding this comment.
logPrint() uses localtime(), which is not thread-safe and can cause data races/corrupted timestamps when logging from multiple threads (this plugin starts several threads). Please use localtime_r() (or gmtime_r()) with a stack-allocated tm instead.
| struct tm* lt; | |
| const char* fileName = trimPath(file); | |
| if (gDefaultLogLevel < level) | |
| return; | |
| gettimeofday(&tv, NULL); | |
| lt = localtime(&tv.tv_sec); | |
| printf("%.2d:%.2d:%.2d.%.6lld [%-5s] [PID=%d] [TID=%d] [%s +%d] %s : %s\n", lt->tm_hour, lt->tm_min, lt->tm_sec, (long long int)tv.tv_usec, levelMap[level], getpid(), gettid(), fileName, line, func, formattedLog); | |
| struct tm lt; | |
| const char* fileName = trimPath(file); | |
| if (gDefaultLogLevel < level) | |
| return; | |
| gettimeofday(&tv, NULL); | |
| localtime_r(&tv.tv_sec, <); | |
| printf("%.2d:%.2d:%.2d.%.6lld [%-5s] [PID=%d] [TID=%d] [%s +%d] %s : %s\n", lt.tm_hour, lt.tm_min, lt.tm_sec, (long long int)tv.tv_usec, levelMap[level], getpid(), gettid(), fileName, line, func, formattedLog); |
| printf("%.2d:%.2d:%.2d.%.6lld [%-5s] [PID=%d] [TID=%d] [%s +%d] %s : %s\n", lt->tm_hour, lt->tm_min, lt->tm_sec, (long long int)tv.tv_usec, levelMap[level], getpid(), gettid(), fileName, line, func, formattedLog); | ||
| fflush(stdout); |
There was a problem hiding this comment.
This code calls gettid(), but gettid() is not a portable/POSIX API and may be unavailable depending on libc/kernel headers, causing build failures. Consider using syscall(SYS_gettid) (since you already include <sys/syscall.h>) or conditionally compiling the TID output.
| #### COM-RPC Interface | ||
| - **INetworkConnectionStats.h** - Defines the COM-RPC interface with: | ||
| - INetworkConnectionStats interface with all network diagnostic methods | ||
| - INotification sub-interface for event notifications | ||
| - Proper interface IDs and stubgen annotations | ||
|
|
||
| #### Plugin Main Class | ||
| - **NetworkConnectionStats.h** - Plugin header with: | ||
| - Inherits from `PluginHost::IPlugin` and `PluginHost::JSONRPC` | ||
| - Notification handler for RPC connection lifecycle and events | ||
| - Interface aggregation for COM-RPC | ||
|
|
||
| - **NetworkConnectionStats.cpp** - Plugin implementation with: | ||
| - IPlugin lifecycle methods (Initialize, Deinitialize, Information) | ||
| - RPC connection management | ||
| - JSON-RPC registration | ||
| - Notification forwarding |
There was a problem hiding this comment.
This conversion document states the plugin inherits PluginHost::JSONRPC, registers JSON-RPC methods, and provides a JSON-RPC API spec. The current codebase implements an internal-only IPlugin + COM-RPC aggregate and the networkstats/definition subdir is not built. Please update the document to reflect the implemented architecture (or implement the described JSON-RPC layer).
| { | ||
| "$schema": "interface.schema.json", | ||
| "jsonrpc": "2.0", | ||
| "info": { | ||
| "title": "NetworkConnectionStats API", | ||
| "class": "NetworkConnectionStats", | ||
| "description": "NetworkConnectionStats plugin for network diagnostics and monitoring" | ||
| }, |
There was a problem hiding this comment.
This JSON-RPC definition describes a public API surface (methods/events) that is not implemented by the current plugin (which is internal-only and does not derive from PluginHost::JSONRPC). Please either update this spec to match the implemented interfaces, or remove/disable it to avoid generating misleading documentation/stubs later.
| #include <atomic> | ||
| #include <queue> | ||
| #include <mutex> | ||
| #include <condition_variable> |
There was a problem hiding this comment.
NetworkConnectionStatsImplementation.h uses std::list (_notificationCallbacks) but does not include . Please add the missing standard header to avoid compile failures depending on indirect includes.
| #include <condition_variable> | |
| #include <condition_variable> | |
| #include <list> |
| #include "INetworkData.h" | ||
| #include "Module.h" | ||
| #include <string> | ||
| #include <core/JSON.h> |
There was a problem hiding this comment.
This header declares std::shared_ptr (m_networkManagerClient) but never includes . Please include here to avoid build failures due to indirect include order.
| #include <core/JSON.h> | |
| #include <core/JSON.h> | |
| #include <memory> | |
| #include <functional> |
|
b'## WARNING: A Blackduck scan failure has been waived A prior failure has been upvoted
|
|
I have checked the fossid failures: There is one match to rdkcentral which is not of interest. The other two matches are from jmanc3/winbar and Karunakaran has pointed out that jmanc3 has used original code from rdkcentral (committed 17-Nov-24) which jmanc3 then committed several months later on 16-Jun-25. So the matches are a non-issue. |
3fd5c23 to
b705641
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 28 changed files in this pull request and generated 9 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| option(ENABLE_LEGACY_PLUGINS "Enable Legacy Plugins" ON) | ||
| option(USE_RDK_LOGGER "Enable RDK Logger for logging" OFF ) | ||
| option(ENABLE_UNIT_TESTING "Enable unit tests" OFF) | ||
| option(USE_TELEMETRY "Enable Telemetry support for NetworkConnectionStats" ON) |
There was a problem hiding this comment.
This top-level option introduces USE_TELEMETRY defaulting to ON. USE_TELEMETRY is also used by other targets in this repo (e.g., tools/upnp), so this change will implicitly enable telemetry (and add a hard dependency on T2) beyond NetworkConnectionStats. Consider defaulting this option to OFF, or using a more scoped option name (e.g., NETWORKSTATS_USE_TELEMETRY) to avoid changing behavior of unrelated components.
|
|
||
| // Notification callbacks | ||
| std::list<Exchange::INetworkConnectionStats::INotification*> _notificationCallbacks; | ||
| Core::CriticalSection _notificationLock; |
There was a problem hiding this comment.
This header uses std::list for _notificationCallbacks but does not include <list>. Please include the required standard header directly here (avoid relying on transitive includes from Thunder headers).
| │ │ - Inherits IPlugin + JSONRPC │ │ | ||
| │ │ - Manages RPC connection │ │ | ||
| │ │ - Forwards JSON-RPC calls │ │ | ||
| │ │ - Handles notifications │ │ | ||
| │ └────────────────┬───────────────────────────────────────────┘ │ | ||
| │ │ COM-RPC │ |
There was a problem hiding this comment.
This quick reference states the plugin inherits PluginHost::JSONRPC and exposes many JSON-RPC methods/events, but the implementation in this PR is IPlugin-only and the COM-RPC interface only defines Configure/Register/Unregister. Please update this document to match the actual code (or implement the documented APIs) to avoid misleading integrators.
| │ │ - Inherits IPlugin + JSONRPC │ │ | |
| │ │ - Manages RPC connection │ │ | |
| │ │ - Forwards JSON-RPC calls │ │ | |
| │ │ - Handles notifications │ │ | |
| │ └────────────────┬───────────────────────────────────────────┘ │ | |
| │ │ COM-RPC │ | |
| │ │ - Implements IPlugin │ │ | |
| │ │ - Manages COM-RPC connection │ │ | |
| │ │ - Uses COM-RPC to talk to implementation │ │ | |
| │ └────────────────┬───────────────────────────────────────────┘ │ | |
| │ │ COM-RPC (INetworkConnectionStats) │ |
| configuration = JSON() | ||
| configuration.add("root", process) | ||
| configuration.add("reportingInterval", "@PLUGIN_NETWORKCONNECTIONSTATS_REPORTING_INTERVAL@") | ||
| configuration.add("autoStart", True) |
There was a problem hiding this comment.
configuration.add("autoStart", True) doesn’t match the format used by other *.conf.in files in this repo (they use quoted strings like "true" or CMake substitutions). True is likely invalid for this config templating and may break config generation/parsing.
| configuration.add("autoStart", True) | |
| configuration.add("autoStart", "@PLUGIN_NETWORKCONNECTIONSTATS_AUTOSTART@") |
| uint32_t Register(INetworkConnectionStats::INotification* notification) override; | ||
| uint32_t Unregister(INetworkConnectionStats::INotification* notification) override; |
There was a problem hiding this comment.
Register/Unregister use INetworkConnectionStats::INotification* but INetworkConnectionStats is declared under WPEFramework::Exchange. As written, this won’t resolve in WPEFramework::Plugin and should be fully qualified (e.g., Exchange::INetworkConnectionStats::INotification*) to match the inherited interface.
| uint32_t Register(INetworkConnectionStats::INotification* notification) override; | |
| uint32_t Unregister(INetworkConnectionStats::INotification* notification) override; | |
| uint32_t Register(Exchange::INetworkConnectionStats::INotification* notification) override; | |
| uint32_t Unregister(Exchange::INetworkConnectionStats::INotification* notification) override; |
| // Create network provider using factory | ||
| m_provider = NetworkDataProviderFactory::CreateProvider(providerType); | ||
| if (m_provider == nullptr) { |
There was a problem hiding this comment.
Configure() assigns a new object to m_provider without first cleaning up any existing provider instance (and any running threads/subscriptions tied to it). If Configure() is called more than once, this will leak and can leave multiple background threads running. Clean up the previous state before replacing m_provider.
|
|
||
| #include "Module.h" | ||
|
|
||
| MODULE_NAME_DECLARATION(BUILD_REFERENCE) |
There was a problem hiding this comment.
MODULE_NAME_DECLARATION(BUILD_REFERENCE) looks inconsistent with the rest of this repo, which uses PLUGIN_BUILD_REFERENCE (and this module’s CMake defines -DPLUGIN_BUILD_REFERENCE=...). If BUILD_REFERENCE isn’t defined, this will fail to compile or embed the wrong build reference.
| MODULE_NAME_DECLARATION(BUILD_REFERENCE) | |
| MODULE_NAME_DECLARATION(PLUGIN_BUILD_REFERENCE) |
| /* Test main function to validate NetworkJsonRPCProvider member functions */ | ||
| int main() | ||
| { | ||
| NetworkJsonRPCProvider provider; |
There was a problem hiding this comment.
The JSON-RPC provider test main() constructs NetworkJsonRPCProvider but never calls Initialize(). As a result, the menu actions will hit "client not initialized" and return empty/false. Call provider.Initialize() once before entering the menu (as done in the COM-RPC provider test).
| NetworkJsonRPCProvider provider; | |
| NetworkJsonRPCProvider provider; | |
| uint32_t initResult = provider.Initialize(); | |
| if (initResult != WPEFramework::Core::ERROR_NONE) { | |
| std::cerr << "Failed to initialize NetworkJsonRPCProvider, error code: " << initResult << std::endl; | |
| return 1; | |
| } |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 28 changed files in this pull request and generated 15 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| * @param interface_name Interface name (e.g., eth0, wlan0) | ||
| * @return IPv4 address string | ||
| */ | ||
| std::string getIpv4Address(std::string interface_name) override; |
There was a problem hiding this comment.
String parameters should be passed by const reference to avoid unnecessary copies. Change std::string interface_name to const std::string& interface_name.
| int choice; | ||
| std::string interface_name; | ||
| bool running = true; | ||
|
|
There was a problem hiding this comment.
The Initialize method should be called before using the provider in the test main function. Add a check and call to provider.Initialize() before running tests to ensure proper initialization.
| const uint32_t initResult = provider.Initialize(); | |
| if (initResult != WPEFramework::Core::ERROR_NONE) { | |
| std::cerr << "Failed to initialize NetworkJsonRPCProvider, error code: " << initResult << std::endl; | |
| return -1; | |
| } |
| if (result.HasLabel("success")) { | ||
| bool success = result["success"].Boolean(); | ||
|
|
||
| // Extract ping statistics |
There was a problem hiding this comment.
Comment mismatch: The comment says "Extract ping statistics" but the immediately following code checks the "success" field which is a Boolean status, not statistics. The actual statistics extraction happens later. Consider updating the comment to accurately reflect the code structure.
| // Extract ping statistics | |
| // Process ping result status and statistics |
| /** | ||
| * If not stated otherwise in this file or this component's LICENSE | ||
| * file the following copyright and licenses apply: | ||
| * | ||
| * Copyright 2025 RDK Management | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| **/ | ||
|
|
||
| #include "ThunderComRPCProvider.h" | ||
| #include "NetworkConnectionStatsLogger.h" | ||
| #include <cstring> | ||
|
|
||
| using namespace std; | ||
| using namespace NetworkConnectionStatsLogger; | ||
|
|
||
| /* @brief Constructor */ | ||
| NetworkComRPCProvider::NetworkComRPCProvider() | ||
| : m_networkManagerClient(nullptr) | ||
| { | ||
| NSLOG_INFO("NetworkComRPCProvider constructed"); | ||
| } | ||
|
|
||
| /* @brief Destructor */ | ||
| NetworkComRPCProvider::~NetworkComRPCProvider() | ||
| { | ||
| m_networkManagerClient.reset(); | ||
| } | ||
|
|
||
| /* @brief Initialize the provider with Thunder COM-RPC connection */ | ||
| bool NetworkComRPCProvider::Initialize() | ||
| { | ||
| try { | ||
| NSLOG_INFO("Creating JSON-RPC client for callsign: %s over COM-RPC", NETWORK_MANAGER_CALLSIGN); | ||
|
|
||
| // For COM-RPC, don't set THUNDER_ACCESS - let it use the default connection | ||
| // The plugin framework handles the routing automatically | ||
| m_networkManagerClient = std::make_shared<WPEFramework::JSONRPC::LinkType<WPEFramework::Core::JSON::IElement>>( | ||
| _T(NETWORK_MANAGER_CALLSIGN) | ||
| ); | ||
|
|
||
| if (m_networkManagerClient) { | ||
| NSLOG_INFO("NetworkManager COM-RPC client initialized successfully"); | ||
| return true; | ||
| } else { | ||
| NSLOG_ERROR("Failed to create NetworkManager COM-RPC client"); | ||
| return false; | ||
| } | ||
| } | ||
| catch (const std::exception& e) { | ||
| NSLOG_ERROR("Exception during initialization: %s", e.what()); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| /* @brief Retrieve IPv4 address for specified interface */ | ||
| std::string NetworkComRPCProvider::getIpv4Address(std::string interface_name) | ||
| { | ||
| std::string ipv4_address = ""; | ||
|
|
||
| // Initialize member variables | ||
| m_ipv4Gateway = ""; | ||
| m_ipv4PrimaryDns = ""; | ||
|
|
||
| if (!m_networkManagerClient) { | ||
| NSLOG_ERROR("NetworkManager client not initialized"); | ||
| return ""; | ||
| } | ||
|
|
||
| WPEFramework::Core::JSON::VariantContainer params; | ||
| params["interface"] = interface_name; | ||
| params["ipversion"] = "IPv4"; | ||
|
|
||
| WPEFramework::Core::JSON::VariantContainer result; | ||
|
|
||
| uint32_t rc = m_networkManagerClient->Invoke<WPEFramework::Core::JSON::VariantContainer, WPEFramework::Core::JSON::VariantContainer>( | ||
| 5000, "GetIPSettings", params, result); | ||
|
|
||
| if (rc == WPEFramework::Core::ERROR_NONE) { | ||
| if (result.HasLabel("ipaddress")) { | ||
| ipv4_address = result["ipaddress"].String(); | ||
| NSLOG_INFO("IPv4 address retrieved: %s", ipv4_address.c_str()); | ||
| } | ||
| if (result.HasLabel("gateway")) { | ||
| m_ipv4Gateway = result["gateway"].String(); | ||
| NSLOG_INFO("IPv4 Route: %s", m_ipv4Gateway.c_str()); | ||
| } | ||
| if (result.HasLabel("primarydns")) { | ||
| m_ipv4PrimaryDns = result["primarydns"].String(); | ||
| NSLOG_INFO("DNS entry: %s", m_ipv4PrimaryDns.c_str()); | ||
| } | ||
| } else { | ||
| NSLOG_ERROR("GetIPSettings COM-RPC call failed with error code: %u", rc); | ||
| } | ||
|
|
||
| return ipv4_address; | ||
| } | ||
|
|
||
| /* @brief Retrieve IPv6 address for specified interface */ | ||
| std::string NetworkComRPCProvider::getIpv6Address(std::string interface_name) | ||
| { | ||
| std::string ipv6_address = ""; | ||
|
|
||
| // Initialize member variables | ||
| m_ipv6Gateway = ""; | ||
| m_ipv6PrimaryDns = ""; | ||
|
|
||
| if (!m_networkManagerClient) { | ||
| NSLOG_ERROR("NetworkManager client not initialized"); | ||
| return ""; | ||
| } | ||
|
|
||
| WPEFramework::Core::JSON::VariantContainer params; | ||
| params["interface"] = interface_name; | ||
| params["ipversion"] = "IPv6"; | ||
|
|
||
| WPEFramework::Core::JSON::VariantContainer result; | ||
|
|
||
| uint32_t rc = m_networkManagerClient->Invoke<WPEFramework::Core::JSON::VariantContainer, WPEFramework::Core::JSON::VariantContainer>( | ||
| 5000, "GetIPSettings", params, result); | ||
|
|
||
| if (rc == WPEFramework::Core::ERROR_NONE) { | ||
| if (result.HasLabel("ipaddress")) { | ||
| ipv6_address = result["ipaddress"].String(); | ||
| NSLOG_INFO("IPv6 address retrieved: %s", ipv6_address.c_str()); | ||
| } | ||
| if (result.HasLabel("gateway")) { | ||
| m_ipv6Gateway = result["gateway"].String(); | ||
| NSLOG_INFO("IPv6 Route: %s", m_ipv6Gateway.c_str()); | ||
| } | ||
| if (result.HasLabel("primarydns")) { | ||
| m_ipv6PrimaryDns = result["primarydns"].String(); | ||
| NSLOG_INFO("DNS entry: %s", m_ipv6PrimaryDns.c_str()); | ||
| } | ||
| } else { | ||
| NSLOG_ERROR("GetIPSettings COM-RPC call failed with error code: %u", rc); | ||
| } | ||
|
|
||
| return ipv6_address; | ||
| } | ||
|
|
||
| /* @brief Get current network connection type */ | ||
| std::string NetworkComRPCProvider::getConnectionType() | ||
| { | ||
| std::string interface = getInterface(); | ||
|
|
||
| if (interface.empty()) { | ||
| NSLOG_ERROR("Error: Unable to retrieve interface"); | ||
| return ""; | ||
| } | ||
|
|
||
| std::string connectionType; | ||
|
|
||
| // Check if interface starts with "eth" or "eno" | ||
| if (interface.find("eth") == 0 || interface.find("eno") == 0) { | ||
| connectionType = "Ethernet"; | ||
| NSLOG_INFO("Connection type: %s (interface: %s)", connectionType.c_str(), interface.c_str()); | ||
| } | ||
| // Check if interface starts with "wlan" | ||
| else if (interface.find("wlan") == 0) { | ||
| connectionType = "WiFi"; | ||
| NSLOG_INFO("Connection type: %s (interface: %s)", connectionType.c_str(), interface.c_str()); | ||
| } | ||
| else { | ||
| connectionType = "Unknown"; | ||
| NSLOG_INFO("Connection type: %s (interface: %s)", connectionType.c_str(), interface.c_str()); | ||
| } | ||
|
|
||
| return connectionType; | ||
| } | ||
|
|
||
| /* @brief Get DNS server entries */ | ||
| std::string NetworkComRPCProvider::getDnsEntries() | ||
| { | ||
| // Return combined DNS entries from IPv4 and IPv6 | ||
| std::string dnsEntries = ""; | ||
|
|
||
| if (!m_ipv4PrimaryDns.empty()) { | ||
| dnsEntries += m_ipv4PrimaryDns; | ||
| } | ||
|
|
||
| if (!m_ipv6PrimaryDns.empty()) { | ||
| if (!dnsEntries.empty()) { | ||
| dnsEntries += ","; | ||
| } | ||
| dnsEntries += m_ipv6PrimaryDns; | ||
| } | ||
|
|
||
| return dnsEntries; | ||
| } | ||
|
|
||
| /* @brief Populate network interface data */ | ||
| void NetworkComRPCProvider::populateNetworkData() | ||
| { | ||
| std::string interface = getInterface(); | ||
|
|
||
| if (!interface.empty()) { | ||
| getIpv4Address(interface); | ||
| getIpv6Address(interface); | ||
| NSLOG_INFO("Network data populated for interface: %s", interface.c_str()); | ||
| } | ||
| } | ||
|
|
||
| /* @brief Get current active interface name */ | ||
| std::string NetworkComRPCProvider::getInterface() | ||
| { | ||
| if (!m_networkManagerClient) { | ||
| NSLOG_ERROR("NetworkManager client not initialized"); | ||
| return ""; | ||
| } | ||
|
|
||
| WPEFramework::Core::JSON::VariantContainer params; | ||
| WPEFramework::Core::JSON::VariantContainer result; | ||
|
|
||
| uint32_t rc = m_networkManagerClient->Invoke<WPEFramework::Core::JSON::VariantContainer, WPEFramework::Core::JSON::VariantContainer>( | ||
| 5000, "GetPrimaryInterface", params, result); | ||
|
|
||
| std::string interface = ""; | ||
| if (rc == WPEFramework::Core::ERROR_NONE) { | ||
| if (result.HasLabel("interface")) { | ||
| interface = result["interface"].String(); | ||
| NSLOG_INFO("Primary interface retrieved: %s", interface.c_str()); | ||
| } | ||
| } else { | ||
| NSLOG_ERROR("GetPrimaryInterface COM-RPC call failed with error code: %u", rc); | ||
| } | ||
|
|
||
| return interface; | ||
| } | ||
|
|
||
| /* @brief Ping to gateway to check packet loss */ | ||
| bool NetworkComRPCProvider::pingToGatewayCheck(std::string endpoint, std::string ipversion, int count, int timeout) | ||
| { | ||
| // Initialize member variables | ||
| m_packetLoss = ""; | ||
| m_avgRtt = ""; | ||
|
|
||
| if (!m_networkManagerClient) { | ||
| NSLOG_ERROR("NetworkManager client not initialized"); | ||
| return false; | ||
| } | ||
|
|
||
| WPEFramework::Core::JSON::VariantContainer params; | ||
| params["endpoint"] = endpoint; | ||
| params["ipversion"] = ipversion; | ||
| params["count"] = count; | ||
| params["timeout"] = timeout; | ||
| params["guid"] = "network-stats-ping"; | ||
|
|
||
| WPEFramework::Core::JSON::VariantContainer result; | ||
|
|
||
| uint32_t rc = m_networkManagerClient->Invoke<WPEFramework::Core::JSON::VariantContainer, WPEFramework::Core::JSON::VariantContainer>( | ||
| 30000, "Ping", params, result); | ||
|
|
||
| if (rc != WPEFramework::Core::ERROR_NONE) { | ||
| NSLOG_ERROR("Ping COM-RPC call failed with error code: %u", rc); | ||
| return false; | ||
| } | ||
|
|
||
| NSLOG_INFO("Ping request sent to %s (%s)", endpoint.c_str(), ipversion.c_str()); | ||
|
|
||
| // Check if ping was successful | ||
| if (result.HasLabel("success")) { | ||
| bool success = result["success"].Boolean(); | ||
|
|
||
| // Extract ping statistics | ||
| if (result.HasLabel("packetsTransmitted")) { | ||
| NSLOG_INFO("Packets transmitted: %d", static_cast<int>(result["packetsTransmitted"].Number())); | ||
| } | ||
| if (result.HasLabel("packetsReceived")) { | ||
| NSLOG_INFO("Packets received: %d", static_cast<int>(result["packetsReceived"].Number())); | ||
| } | ||
| if (result.HasLabel("packetLoss")) { | ||
| m_packetLoss = result["packetLoss"].String(); | ||
| NSLOG_INFO("Packet loss: %s", m_packetLoss.c_str()); | ||
| } | ||
| if (result.HasLabel("tripMin")) { | ||
| NSLOG_INFO("RTT min: %s ms", result["tripMin"].String().c_str()); | ||
| } | ||
| if (result.HasLabel("tripAvg")) { | ||
| m_avgRtt = result["tripAvg"].String(); | ||
| NSLOG_INFO("RTT avg: %s ms", m_avgRtt.c_str()); | ||
| } | ||
| if (result.HasLabel("tripMax")) { | ||
| NSLOG_INFO("RTT max: %s ms", result["tripMax"].String().c_str()); | ||
| } | ||
|
|
||
| return success; | ||
| } | ||
| else { | ||
| NSLOG_ERROR("Error: 'success' field not found in response"); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| /* @brief Get IPv4 gateway/route address from last getIpv4Address call */ | ||
| std::string NetworkComRPCProvider::getIpv4Gateway() | ||
| { | ||
| return m_ipv4Gateway; | ||
| } | ||
|
|
||
| /* @brief Get IPv6 gateway/route address from last getIpv6Address call */ | ||
| std::string NetworkComRPCProvider::getIpv6Gateway() | ||
| { | ||
| return m_ipv6Gateway; | ||
| } | ||
|
|
||
| /* @brief Get IPv4 primary DNS from last getIpv4Address call */ | ||
| std::string NetworkComRPCProvider::getIpv4PrimaryDns() | ||
| { | ||
| return m_ipv4PrimaryDns; | ||
| } | ||
|
|
||
| /* @brief Get IPv6 primary DNS from last getIpv6Address call */ | ||
| std::string NetworkComRPCProvider::getIpv6PrimaryDns() | ||
| { | ||
| return m_ipv6PrimaryDns; | ||
| } | ||
|
|
||
| /* @brief Get packet loss from last ping call */ | ||
| std::string NetworkComRPCProvider::getPacketLoss() | ||
| { | ||
| return m_packetLoss; | ||
| } | ||
|
|
||
| /* @brief Get average RTT from last ping call */ | ||
| std::string NetworkComRPCProvider::getAvgRtt() | ||
| { | ||
| return m_avgRtt; | ||
| } | ||
|
|
||
| /* @brief Subscribe to NetworkManager events */ | ||
| uint32_t NetworkComRPCProvider::SubscribeToEvent(const std::string& eventName, | ||
| std::function<void(const WPEFramework::Core::JSON::VariantContainer&)> callback) | ||
| { | ||
| if (!m_networkManagerClient) { | ||
| NSLOG_ERROR("NetworkManager client not initialized"); | ||
| return WPEFramework::Core::ERROR_UNAVAILABLE; | ||
| } | ||
|
|
||
| try { | ||
| // Subscribe to the event using Thunder's Subscribe API | ||
| // Note: The callback will be invoked when the event is received | ||
| uint32_t result = m_networkManagerClient->Subscribe<WPEFramework::Core::JSON::VariantContainer>( | ||
| 5000, // timeout in milliseconds | ||
| eventName, | ||
| [callback](const WPEFramework::Core::JSON::VariantContainer& parameters) { | ||
| callback(parameters); | ||
| } | ||
| ); | ||
|
|
||
| if (result == WPEFramework::Core::ERROR_NONE) { | ||
| NSLOG_INFO("Successfully subscribed to event: %s", eventName.c_str()); | ||
| } else { | ||
| NSLOG_ERROR("Failed to subscribe to event %s, error code: %u", eventName.c_str(), result); | ||
| } | ||
|
|
||
| return result; | ||
| } catch (const std::exception& e) { | ||
| NSLOG_ERROR("Exception subscribing to event %s: %s", eventName.c_str(), e.what()); | ||
| return WPEFramework::Core::ERROR_GENERAL; | ||
| } | ||
| } |
There was a problem hiding this comment.
Code duplication: ThunderJsonRPCProvider and ThunderComRPCProvider have nearly identical implementations for all methods except the client initialization. Consider creating a base class or using a template pattern to reduce duplication. The getIpv4Address, getIpv6Address, getConnectionType, getInterface, pingToGatewayCheck, and other methods are duplicated between the two files with only minor differences in the client type.
|
|
||
| uint32_t NetworkConnectionStatsImplementation::Configure(const string configLine) | ||
| { | ||
| NSLOG_INFO("NetworkConnectionStatsImplementation::Configure"); |
There was a problem hiding this comment.
Missing error handling: If m_provider is deleted due to initialization failure but Configure() is called again (which shouldn't happen but could in error scenarios), m_provider will be nullptr and any subsequent access will cause crashes. Consider adding a check at the beginning of Configure() to prevent re-configuration or handle the re-initialization case properly.
| NSLOG_INFO("NetworkConnectionStatsImplementation::Configure"); | |
| NSLOG_INFO("NetworkConnectionStatsImplementation::Configure"); | |
| // Ensure provider is available before proceeding. If this occurs, | |
| // it likely indicates an earlier initialization failure or misuse. | |
| if (m_provider == nullptr) { | |
| NSLOG_ERROR("Configure called while provider is not initialized (m_provider is nullptr)"); | |
| return Core::ERROR_GENERAL; | |
| } |
| if (result.HasLabel("success")) { | ||
| bool success = result["success"].Boolean(); | ||
|
|
||
| // Extract ping statistics from JSON |
There was a problem hiding this comment.
Comment mismatch: The comment says "Extract ping statistics from JSON" but the field being accessed is "success" which is a Boolean, not statistics. Consider updating the comment to accurately reflect the code flow.
| // Extract ping statistics from JSON | |
| // Retrieve ping result status and statistics from JSON response |
| std::cout << "Primary DNS: " << (primaryDns.empty() ? "(empty)" : primaryDns) << "\n"; | ||
| } | ||
|
|
||
| std::cout << "\n[Test 6/7] getIpv6Address()\n"; |
There was a problem hiding this comment.
Test count mismatch: The text says "Test 6/7" but there are only 6 tests shown (getInterface, getIpv4Address, getIpv6Address, getConnectionType, getDnsEntries, populateNetworkData, pingToGatewayCheck). Either the label should be "Test 6/7" or the comment should reflect the actual number of tests.
| std::cout << "\n[Test 6/7] getIpv6Address()\n"; | |
| std::cout << "\n[Test 6/6] getIpv6Address()\n"; |
| { | ||
| try { | ||
| // Set Thunder access point | ||
| WPEFramework::Core::SystemInfo::SetEnvironment(_T("THUNDER_ACCESS"), _T("127.0.0.1:9998")); |
There was a problem hiding this comment.
Security concern: The THUNDER_ACCESS environment variable is hardcoded to "127.0.0.1:9998". If this plugin runs in an environment where Thunder is bound to a different address or port, or if the environment variable is already set, this could cause connection failures or unexpected behavior. Consider making this configurable through the plugin configuration or checking if the environment variable is already set before overwriting it.
| std::string getIpv4Address(std::string interface_name) override; | ||
|
|
||
| /* @brief Retrieve IPv6 address for specified interface | ||
| * @param interface_name Interface name (e.g., eth0, wlan0) | ||
| * @return IPv6 address string | ||
| */ | ||
| std::string getIpv6Address(std::string interface_name) override; | ||
|
|
||
| /* @brief Get IPv4 gateway/route address from last getIpv4Address call */ | ||
| std::string getIpv4Gateway() override; | ||
|
|
||
| /* @brief Get IPv6 gateway/route address from last getIpv6Address call */ | ||
| std::string getIpv6Gateway() override; | ||
|
|
||
| /* @brief Get IPv4 primary DNS from last getIpv4Address call */ | ||
| std::string getIpv4PrimaryDns() override; | ||
|
|
||
| /* @brief Get IPv6 primary DNS from last getIpv6Address call */ | ||
| std::string getIpv6PrimaryDns() override; | ||
|
|
||
| /* @brief Get current network connection type */ | ||
| std::string getConnectionType() override; | ||
|
|
||
| /* @brief Get DNS server entries */ | ||
| std::string getDnsEntries() override; | ||
|
|
||
| /* @brief Populate network interface data */ | ||
| void populateNetworkData() override; | ||
|
|
||
| /* @brief Get current active interface name */ | ||
| std::string getInterface() override; | ||
|
|
||
| /* @brief Ping to gateway to check packet loss | ||
| * @param endpoint Gateway IP address to ping | ||
| * @param ipversion Either "IPv4" or "IPv6" | ||
| * @param count Number of ping packets to send | ||
| * @param timeout Timeout in seconds | ||
| * @return true if ping successful, false otherwise | ||
| */ | ||
| bool pingToGatewayCheck(std::string endpoint, std::string ipversion, int count, int timeout) override; | ||
|
|
||
| /* @brief Get packet loss from last ping call */ | ||
| std::string getPacketLoss() override; | ||
|
|
||
| /* @brief Get average RTT from last ping call */ | ||
| std::string getAvgRtt() override; | ||
|
|
||
| /* @brief Subscribe to NetworkManager events | ||
| * @param eventName Name of the event (e.g., "onInterfaceStateChange") | ||
| * @param callback Callback function to be called when event fires | ||
| * @return Error code (Core::ERROR_NONE on success) | ||
| */ | ||
| uint32_t SubscribeToEvent(const std::string& eventName, | ||
| std::function<void(const WPEFramework::Core::JSON::VariantContainer&)> callback) override; |
There was a problem hiding this comment.
String parameters should be passed by const reference to avoid unnecessary copies. Change all std::string parameters to const std::string& throughout the interface methods.
| // Clean up the partially initialized provider to prevent leaks and later accidental use | ||
| delete m_provider; | ||
| m_provider = nullptr; | ||
| result = Core::ERROR_GENERAL; |
There was a problem hiding this comment.
Potential memory leak: When initialization fails (lines 176-177 and 209-211), m_provider is deleted but not set to nullptr. If Configure is called again or if other methods check m_provider before deletion in the destructor, this could cause use-after-free or double-free issues. After deleting m_provider, set it to nullptr.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
d9a28a8 to
c555c21
Compare
No description provided.