From c3693f8b6a686eb653929fe414fd77c67934390b Mon Sep 17 00:00:00 2001 From: Joel Guittet Date: Thu, 20 Jun 2024 17:16:48 +0200 Subject: [PATCH] core: client: add network connect/disconnect callbacks --- add-ons/src/mender-configure.c | 19 ++++-- add-ons/src/mender-inventory.c | 14 +++++ add-ons/src/mender-troubleshoot.c | 12 ++++ core/src/mender-client.c | 97 ++++++++++++++++++++++++++++++- include/mender-client.h | 14 +++++ tests/src/main.c | 36 +++++++++++- 6 files changed, 185 insertions(+), 7 deletions(-) diff --git a/add-ons/src/mender-configure.c b/add-ons/src/mender-configure.c index c1428d7..5299c89 100644 --- a/add-ons/src/mender-configure.c +++ b/add-ons/src/mender-configure.c @@ -367,25 +367,31 @@ mender_configure_work_function(void) { return ret; } + /* Request access to the network */ + if (MENDER_OK != (ret = mender_client_network_connect())) { + mender_log_error("Requesting access to the network failed"); + goto END; + } + #ifndef CONFIG_MENDER_CLIENT_CONFIGURE_STORAGE /* Download configuration */ mender_keystore_t *configuration = NULL; if (MENDER_OK != (ret = mender_api_download_configuration_data(&configuration))) { mender_log_error("Unable to get configuration data"); - goto END; + goto RELEASE; } /* Release previous configuration */ if (MENDER_OK != (ret = mender_utils_keystore_delete(mender_configure_keystore))) { mender_log_error("Unable to delete device configuration"); - goto END; + goto RELEASE; } /* Update device configuration */ if (MENDER_OK != (ret = mender_utils_keystore_copy(&mender_configure_keystore, configuration))) { mender_log_error("Unable to update device configuration"); - goto END; + goto RELEASE; } /* Invoke the update callback */ @@ -400,10 +406,15 @@ mender_configure_work_function(void) { mender_log_error("Unable to publish configuration data"); } -#ifndef CONFIG_MENDER_CLIENT_CONFIGURE_STORAGE +RELEASE: + + /* Release access to the network */ + mender_client_network_release(); END: +#ifndef CONFIG_MENDER_CLIENT_CONFIGURE_STORAGE + /* Release memeory */ mender_utils_keystore_delete(configuration); diff --git a/add-ons/src/mender-inventory.c b/add-ons/src/mender-inventory.c index b218edc..76435f8 100755 --- a/add-ons/src/mender-inventory.c +++ b/add-ons/src/mender-inventory.c @@ -26,6 +26,7 @@ */ #include "mender-api.h" +#include "mender-client.h" #include "mender-inventory.h" #include "mender-log.h" #include "mender-scheduler.h" @@ -205,11 +206,24 @@ mender_inventory_work_function(void) { return ret; } + /* Request access to the network */ + if (MENDER_OK != (ret = mender_client_network_connect())) { + mender_log_error("Requesting access to the network failed"); + goto END; + } + /* Publish inventory */ if (MENDER_OK != (ret = mender_api_publish_inventory_data(mender_inventory_keystore))) { mender_log_error("Unable to publish inventory data"); } +RELEASE: + + /* Release access to the network */ + mender_client_network_release(); + +END: + /* Release mutex used to protect access to the inventory key-store */ mender_scheduler_mutex_give(mender_inventory_mutex); diff --git a/add-ons/src/mender-troubleshoot.c b/add-ons/src/mender-troubleshoot.c index 3646703..4755d25 100644 --- a/add-ons/src/mender-troubleshoot.c +++ b/add-ons/src/mender-troubleshoot.c @@ -383,6 +383,9 @@ mender_troubleshoot_deactivate(void) { mender_log_error("Unable to disconnect the device of the server"); } mender_troubleshoot_handle = NULL; + + /* Release access to the network */ + mender_client_network_release(); } /* Release session ID */ @@ -511,6 +514,12 @@ mender_troubleshoot_healthcheck_work_function(void) { } else { + /* Request access to the network */ + if (MENDER_OK != (ret = mender_client_network_connect())) { + mender_log_error("Requesting access to the network failed"); + goto END; + } + /* Connect the device to the server */ if (MENDER_OK != (ret = mender_api_troubleshoot_connect(&mender_troubleshoot_data_received_callback, &mender_troubleshoot_handle))) { mender_log_error("Unable to connect the device to the server"); @@ -541,6 +550,9 @@ mender_troubleshoot_healthcheck_work_function(void) { mender_log_error("Unable to disconnect the device of the server"); } mender_troubleshoot_handle = NULL; + + /* Release access to the network */ + mender_client_network_release(); } /* Release session ID */ diff --git a/core/src/mender-client.c b/core/src/mender-client.c index cccf943..389bb80 100644 --- a/core/src/mender-client.c +++ b/core/src/mender-client.c @@ -85,6 +85,12 @@ typedef enum { */ static mender_client_state_t mender_client_state = MENDER_CLIENT_STATE_INITIALIZATION; +/** + * @brief Counter and mutex for the management of network connect/release callbacks + */ +static uint8_t mender_client_network_count = 0; +static void * mender_client_network_mutex = NULL; + /** * @brief Deployment data (ID, artifact name and payload types), used to report deployment status after rebooting */ @@ -291,6 +297,12 @@ mender_client_init(mender_client_config_t *config, mender_client_callbacks_t *ca goto END; } + /* Create network management mutex */ + if (MENDER_OK != (ret = mender_scheduler_mutex_create(&mender_client_network_mutex))) { + mender_log_error("Unable to create network management mutex"); + return ret; + } + /* Create artifact types management mutex */ if (MENDER_OK != (ret = mender_scheduler_mutex_create(&mender_client_artifact_types_mutex))) { mender_log_error("Unable to create artifact types management mutex"); @@ -490,6 +502,74 @@ mender_client_execute(void) { return ret; } +mender_err_t +mender_client_network_connect(void) { + + mender_err_t ret; + + /* Take mutex used to protect access to the network management counter */ + if (MENDER_OK != (ret = mender_scheduler_mutex_take(mender_client_network_mutex, -1))) { + mender_log_error("Unable to take mutex"); + return ret; + } + + /* Check the network management counter value */ + if (0 == mender_client_network_count) { + + /* Request network access */ + if (NULL != mender_client_callbacks.network_connect) { + if (MENDER_OK != (ret = mender_client_callbacks.network_connect())) { + mender_log_error("Unable to connect network"); + goto END; + } + } + } + + /* Increment network management counter */ + mender_client_network_count++; + +END: + + /* Release mutex used to protect access to the network management counter */ + mender_scheduler_mutex_give(mender_client_network_mutex); + + return ret; +} + +mender_err_t +mender_client_network_release(void) { + + mender_err_t ret; + + /* Take mutex used to protect access to the network management counter */ + if (MENDER_OK != (ret = mender_scheduler_mutex_take(mender_client_network_mutex, -1))) { + mender_log_error("Unable to take mutex"); + return ret; + } + + /* Decrement network management counter */ + mender_client_network_count--; + + /* Check the network management counter value */ + if (0 == mender_client_network_count) { + + /* Release network access */ + if (NULL != mender_client_callbacks.network_release) { + if (MENDER_OK != (ret = mender_client_callbacks.network_release())) { + mender_log_error("Unable to release network"); + goto END; + } + } + } + +END: + + /* Release mutex used to protect access to the network management counter */ + mender_scheduler_mutex_give(mender_client_network_mutex); + + return ret; +} + mender_err_t mender_client_exit(void) { @@ -533,6 +613,10 @@ mender_client_exit(void) { mender_client_config.tenant_token = NULL; mender_client_config.authentication_poll_interval = 0; mender_client_config.update_poll_interval = 0; + mender_client_network_count = 0; + mender_scheduler_mutex_give(mender_client_network_mutex); + mender_scheduler_mutex_delete(mender_client_network_mutex); + mender_client_network_mutex = NULL; if (NULL != mender_client_deployment_data) { cJSON_Delete(mender_client_deployment_data); mender_client_deployment_data = NULL; @@ -574,16 +658,20 @@ mender_client_work_function(void) { /* Update client state */ mender_client_state = MENDER_CLIENT_STATE_AUTHENTICATION; } + /* Request access to the network */ + if (MENDER_OK != (ret = mender_client_network_connect())) { + goto END; + } /* Intentional pass-through */ if (MENDER_CLIENT_STATE_AUTHENTICATION == mender_client_state) { /* Perform authentication with the server */ if (MENDER_DONE != (ret = mender_client_authentication_work_function())) { - goto END; + goto RELEASE; } /* Update work period */ if (MENDER_OK != (ret = mender_scheduler_work_set_period(mender_client_work_handle, mender_client_config.update_poll_interval))) { mender_log_error("Unable to set work period"); - goto END; + goto RELEASE; } /* Update client state */ mender_client_state = MENDER_CLIENT_STATE_AUTHENTICATED; @@ -594,6 +682,11 @@ mender_client_work_function(void) { ret = mender_client_update_work_function(); } +RELEASE: + + /* Release access to the network */ + mender_client_network_release(); + END: return ret; diff --git a/include/mender-client.h b/include/mender-client.h index bda5024..8d2502a 100644 --- a/include/mender-client.h +++ b/include/mender-client.h @@ -53,6 +53,8 @@ typedef struct { * @brief Mender client callbacks */ typedef struct { + mender_err_t (*network_connect)(void); /**< Invoked when mender-client requests access to the network */ + mender_err_t (*network_release)(void); /**< Invoked when mender-client releases access to the network */ mender_err_t (*authentication_success)(void); /**< Invoked when authentication with the mender server succeeded */ mender_err_t (*authentication_failure)(void); /**< Invoked when authentication with the mender server failed */ mender_err_t (*deployment_status)(mender_deployment_status_t, char *); /**< Invoked on transition changes to inform of the new deployment status */ @@ -116,6 +118,18 @@ mender_err_t mender_client_deactivate(void); */ mender_err_t mender_client_execute(void); +/** + * @brief Function to be called from add-ons to request network access + * @return MENDER_OK if network is connected following the request, error code otherwise + */ +mender_err_t mender_client_network_connect(void); + +/** + * @brief Function to be called from add-ons to release network access + * @return MENDER_OK if network is released following the request, error code otherwise + */ +mender_err_t mender_client_network_release(void); + /** * @brief Release mender client * @return MENDER_OK if the function succeeds, error code otherwise diff --git a/tests/src/main.c b/tests/src/main.c index af07a30..0350f71 100644 --- a/tests/src/main.c +++ b/tests/src/main.c @@ -49,6 +49,38 @@ static const struct option mender_client_options[] = { { "help", 0, NULL, 'h' }, static pthread_mutex_t mender_client_events_mutex; static pthread_cond_t mender_client_events_cond; +/** + * @brief Network connnect callback + * @return MENDER_OK if network is connected following the request, error code otherwise + */ +static mender_err_t +network_connect_cb(void) { + + mender_log_info("Mender client connect network"); + + /* This callback can be used to configure network connection */ + /* Note that the application can connect the network before if required */ + /* This callback only indicates the mender-client requests network access now */ + /* Nothing to do in this test application just return network is available */ + return MENDER_OK; +} + +/** + * @brief Network release callback + * @return MENDER_OK if network is released following the request, error code otherwise + */ +static mender_err_t +network_release_cb(void) { + + mender_log_info("Mender client released network"); + + /* This callback can be used to release network connection */ + /* Note that the application can keep network activated if required */ + /* This callback only indicates the mender-client doesn't request network access now */ + /* Nothing to do in this test application just return network is released */ + return MENDER_OK; +} + /** * @brief Authentication success callback * @return MENDER_OK if application is marked valid and success deployment status should be reported to the server, error code otherwise @@ -454,7 +486,9 @@ main(int argc, char **argv) { .authentication_poll_interval = 0, .update_poll_interval = 0, .recommissioning = false }; - mender_client_callbacks_t mender_client_callbacks = { .authentication_success = authentication_success_cb, + mender_client_callbacks_t mender_client_callbacks = { .network_connect = network_connect_cb, + .network_release = network_release_cb, + .authentication_success = authentication_success_cb, .authentication_failure = authentication_failure_cb, .deployment_status = deployment_status_cb, .restart = restart_cb };