From 6cedf7c18462fa51df33f2ea639369ca84809609 Mon Sep 17 00:00:00 2001 From: Joel Guittet Date: Wed, 29 Mar 2023 17:49:00 +0200 Subject: [PATCH] architecture: rework to introduce add-ons and workqueue #8 --- .clang-format-ignore | 1 + .github/workflows/check_include_guards.sh | 4 +- .github/workflows/ci.yml | 4 +- CMakeLists.txt | 9 +- README.md | 31 +- VERSION | 2 +- add-ons/src/mender-inventory.c | 232 +++++++++ core/src/mender-api.c | 178 +++---- core/src/mender-client.c | 475 +++++++++---------- esp-idf/CMakeLists.txt | 50 ++ esp-idf/Kconfig | 71 +++ include/mender-api.h | 24 +- include/mender-client.h | 14 +- include/mender-common.h | 13 +- include/mender-http.h | 4 +- include/mender-inventory.h | 88 ++++ include/mender-log.h | 6 +- include/mender-ota.h | 4 +- include/mender-rtos.h | 93 ++-- include/mender-storage.h | 4 +- include/mender-tls.h | 4 +- include/mender-untar.h | 4 +- include/mender-utils.h | 4 +- platform/http/zephyr/src/mender-http.c | 22 +- platform/rtos/freertos/src/mender-rtos.c | 346 +++++++++++++- platform/rtos/zephyr/src/mender-rtos.c | 304 ++++++++++-- platform/tls/mbedtls/src/mender-tls.c | 36 +- tests/CMakeLists.txt | 19 +- tests/mocks/CMakeLists.txt | 10 +- tests/mocks/cjson/CMakeLists.txt | 13 +- tests/mocks/esp-idf/CMakeLists.txt | 4 +- tests/mocks/freertos/CMakeLists.txt | 32 +- tests/mocks/mbedtls/CMakeLists.txt | 15 +- tests/mocks/zephyr/CMakeLists.txt | 4 +- tests/mocks/zephyr/include/zephyr/kernel.h | 37 ++ tests/mocks/zephyr/include/zephyr/sys/util.h | 6 + tests/mocks/zephyr/src/kernel.c | 52 ++ tests/src/main.c | 14 +- zephyr/CMakeLists.txt | 27 +- zephyr/Kconfig | 101 ++-- 40 files changed, 1742 insertions(+), 619 deletions(-) mode change 100644 => 100755 .github/workflows/ci.yml mode change 100644 => 100755 CMakeLists.txt mode change 100644 => 100755 README.md create mode 100755 add-ons/src/mender-inventory.c mode change 100644 => 100755 core/src/mender-api.c mode change 100644 => 100755 core/src/mender-client.c create mode 100755 esp-idf/CMakeLists.txt create mode 100755 esp-idf/Kconfig mode change 100644 => 100755 include/mender-api.h mode change 100644 => 100755 include/mender-client.h mode change 100644 => 100755 include/mender-common.h mode change 100644 => 100755 include/mender-http.h create mode 100755 include/mender-inventory.h mode change 100644 => 100755 include/mender-log.h mode change 100644 => 100755 include/mender-ota.h mode change 100644 => 100755 include/mender-rtos.h mode change 100644 => 100755 include/mender-storage.h mode change 100644 => 100755 include/mender-tls.h mode change 100644 => 100755 include/mender-untar.h mode change 100644 => 100755 include/mender-utils.h mode change 100644 => 100755 platform/http/zephyr/src/mender-http.c mode change 100644 => 100755 platform/rtos/zephyr/src/mender-rtos.c mode change 100644 => 100755 platform/tls/mbedtls/src/mender-tls.c mode change 100644 => 100755 tests/CMakeLists.txt mode change 100644 => 100755 tests/mocks/CMakeLists.txt mode change 100644 => 100755 tests/mocks/cjson/CMakeLists.txt mode change 100644 => 100755 tests/mocks/esp-idf/CMakeLists.txt mode change 100644 => 100755 tests/mocks/freertos/CMakeLists.txt mode change 100644 => 100755 tests/mocks/mbedtls/CMakeLists.txt mode change 100644 => 100755 tests/mocks/zephyr/CMakeLists.txt create mode 100644 tests/mocks/zephyr/include/zephyr/sys/util.h mode change 100644 => 100755 zephyr/CMakeLists.txt diff --git a/.clang-format-ignore b/.clang-format-ignore index 73e1df6..80df972 100644 --- a/.clang-format-ignore +++ b/.clang-format-ignore @@ -26,6 +26,7 @@ tests/mocks/zephyr/include/zephyr/net/socket.h tests/mocks/zephyr/include/zephyr/net/tls_credentials.h tests/mocks/zephyr/include/zephyr/storage/flash_map.h tests/mocks/zephyr/include/zephyr/sys/reboot.h +tests/mocks/zephyr/include/zephyr/sys/util.h tests/mocks/zephyr/include/zephyr/sys/util_macro.h tests/mocks/zephyr/include/zephyr/sys_clock.h tests/mocks/zephyr/src/device.c diff --git a/.github/workflows/check_include_guards.sh b/.github/workflows/check_include_guards.sh index 351c2b6..e64038c 100755 --- a/.github/workflows/check_include_guards.sh +++ b/.github/workflows/check_include_guards.sh @@ -31,11 +31,11 @@ result="" for source_file in `git ls-tree -r HEAD --name-only | grep -E '(.*\.h$|.*\.hpp$)' | grep -vFf .clang-format-ignore` do uppercase=$(echo $(basename ${source_file^^}) | tr '.' '_' | tr '-' '_') - pcregrep -Me "#ifndef __${uppercase}__\n#define __${uppercase}__\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif" ${source_file} > /dev/null 2>&1 + pcregrep -Me "#ifndef __${uppercase}__\n#define __${uppercase}__\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif /\* __cplusplus \*/" ${source_file} > /dev/null 2>&1 if [[ ! $? -eq 0 ]]; then result="${result}\n${source_file}" else - pcregrep -Me "#ifdef __cplusplus\n}\n#endif\n\n#endif /\* __${uppercase}__ \*/" ${source_file} > /dev/null 2>&1 + pcregrep -Me "#ifdef __cplusplus\n}\n#endif /\* __cplusplus \*/\n\n#endif /\* __${uppercase}__ \*/" ${source_file} > /dev/null 2>&1 if [[ ! $? -eq 0 ]]; then result="${result}\n${source_file}" fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml old mode 100644 new mode 100755 index 805ea07..3d0d2fc --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,8 @@ on: - '**' pull_request: types: [opened, synchronize, reopened] + schedule: + - cron: '0 0 1 * *' jobs: check: name: Check code format @@ -70,4 +72,4 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | - sonar-scanner -Dsonar.organization=joelguittet -Dsonar.projectKey=joelguittet_mender-mcu-client -Dsonar.cfamily.cache.enabled=false -Dsonar.inclusions=include/**/*,core/**/*,platform/**/* -Dsonar.projectVersion=$(cat VERSION) -Dsonar.cfamily.build-wrapper-output=bw_output + sonar-scanner -Dsonar.organization=joelguittet -Dsonar.projectKey=joelguittet_mender-mcu-client -Dsonar.inclusions=include/**/*,core/**/*,platform/**/* -Dsonar.projectVersion=$(cat VERSION) -Dsonar.cfamily.build-wrapper-output=bw_output diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100644 new mode 100755 index 621dd57..62ed4ff --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,14 @@ cmake_minimum_required(VERSION 3.16.3) # List of sources -file(GLOB SOURCES_TEMP "${CMAKE_CURRENT_LIST_DIR}/core/src/*.c" "${CMAKE_CURRENT_LIST_DIR}/platform/board/${CONFIG_MENDER_MCU_CLIENT_BOARD_TYPE}/src/*.c" "${CMAKE_CURRENT_LIST_DIR}/platform/http/${CONFIG_MENDER_MCU_CLIENT_HTTP_TYPE}/src/*.c" "${CMAKE_CURRENT_LIST_DIR}/platform/rtos/${CONFIG_MENDER_MCU_CLIENT_RTOS_TYPE}/src/*.c" "${CMAKE_CURRENT_LIST_DIR}/platform/tls/${CONFIG_MENDER_MCU_CLIENT_TLS_TYPE}/src/*.c") +file(GLOB SOURCES_TEMP + "${CMAKE_CURRENT_LIST_DIR}/core/src/*.c" + "${CMAKE_CURRENT_LIST_DIR}/platform/board/${CONFIG_MENDER_MCU_CLIENT_BOARD_TYPE}/src/*.c" + "${CMAKE_CURRENT_LIST_DIR}/platform/http/${CONFIG_MENDER_MCU_CLIENT_HTTP_TYPE}/src/*.c" + "${CMAKE_CURRENT_LIST_DIR}/platform/rtos/${CONFIG_MENDER_MCU_CLIENT_RTOS_TYPE}/src/*.c" + "${CMAKE_CURRENT_LIST_DIR}/platform/tls/${CONFIG_MENDER_MCU_CLIENT_TLS_TYPE}/src/*.c" + "${CMAKE_CURRENT_LIST_DIR}/add-ons/src/*.c" +) # Add include directory include_directories(${CMAKE_CURRENT_LIST_DIR}/include) diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 3a934a2..335d9a1 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ To start using mender-mcu-client, we recommend that you begin with one of the ex The mender-mcu-client library tries to be very light in order to be used on most projects wanted to have over-the-air updates. The library mainly rely on the presence of an RTOS to have thread, semaphore and memory allocation support. -Additionally, an IP interface is required because communications are done using HTTPS protocol, however it is possible to have an abstraction for example using a WiFi module connected to the MCU with a serial interface and using AT commands. +Additionally, a TCP/IP interface is required because communications are done using HTTPS protocol, however it is possible to have an abstraction for example using a WiFi module connected to the MCU with a serial interface and using AT commands. And finally, 4kB of storage should be reserved to save client private and public keys used for authentication with mender server, plus OTA ID and artifact name to be deployed when an update is done (this is used internally to perform OTA report to the server). @@ -79,7 +79,7 @@ This is why I welcome and ask for your contributions. You can for example: * provide new platforms support (please read following Architecture section). * enhance the current implementation of platform support submitting a Pull Request (please pay attention to the results of the analysis done by the CI, code quality is important). * open an issue if you encountered a problem with the existing implementation, and optionally provide a pull request to fix it, this will be reviewed with care. -* open an issue to discuss a new feature you want (please read following Roadmap section to avoid claiming features that are already in the list). +* open an issue to discuss a new feature you want (please read following Roadmap section to avoid claiming features that are already in the list, but of course you are free to open an issue to discuss of future features to ask for the progress or just inform about your interest). * ... @@ -87,10 +87,10 @@ This is why I welcome and ask for your contributions. You can for example: The organization of the source code permits the mender-mcu-client library to provide support for different kind of platforms and projects. -The source code is separate in two main directories: +The source code is separate into three main directories: * `core` contains the main source files providing the implementation of the mender-mcu-client: * `mender-client`: periodically check the availability of updates. - * `mender-api`: the implementation of the mender [Device API](https://docs.mender.io/api/#device-apis). + * `mender-api`: the implementation of the mender [Device APIs](https://docs.mender.io/api/#device-apis). * `mender-untar`: TAR parser to read [mender artifact](https://github.com/mendersoftware/mender-artifact/blob/master/Documentation/artifact-format-v3.md). * `mender-utils`: utilities. * `platform` contains source files specific to the platform or project, it is separated in several sub-directories for each feature of the client that rely on external dependency specific to the platforms: @@ -98,12 +98,23 @@ The source code is separate in two main directories: * `mender-storage`: provide storage area for the mender-client. * `mender-log`: logging API. * `mender-http`: implementation of HTTP client. - * `mender-rtos`: implementation of RTOS functions. + * `mender-rtos`: implementation of RTOS related functions. * `mender-tls`: provide TLS support. +* `add-ons` contains source files of the mender add-ons: + * `mender-inventory`: provide inventory key/value pairs to display inventory data on the mender server. This add-on is highly recommended and should be included by default. It is proposed as an add-on only to give the possibility to reduce the final code size for user who don't need it. The `include` directory contains the header files that define the prototypes for each module of the library. They are common to all platforms and projects and they define the API to be used or implemented. -The usage of the mender-mcu-client library should be to include all the `core` source files each time, and then to pick up wanted platform implementations, or to write your owns (but it's better to share them and open a Pull Request!). For example you may want to use esp-idf with mbedTLS or with a secure element, or using an other RTOS I have not integrated. The combinaisons are infinite. +The usage of the mender-mcu-client library should be to include all the `core` source files each time, and then to pick up wanted platform implementations, or to write your owns (but it's better to share them and open a Pull Request!). For example you may want to use esp-idf with mbedTLS or with a secure element, or using an other RTOS I have not integrated, or maybe you want to have mender storage located in an external EEPROM. The combinaisons are infinite. + +The usage of the add-ons is at your own discretion, they are independant. + +The final code size of the mender-mcu-client library depends of your configuration, the following table gives an estimation. + +| | Code size (-Og) | Code size (-Os) | +|:---------------------|:----------------|:----------------| +| mender-client (core) | 22KB | 20KB | +| mender-inventory | 2KB | 2KB | ## Roadmap @@ -111,10 +122,10 @@ The usage of the mender-mcu-client library should be to include all the `core` s The following features are currently in the pipeline. Please note that I haven't set dates in this roadmap because I develop this in my free time, but you can contribute with Pull Requests: * Support update of [modules](https://docs.mender.io/artifact-creation/create-a-custom-update-module) to perform other kind of updates that could be specific to one project: files, images, etc. -* Integration of other nice to have Mender APIs: [Device Configure](https://docs.mender.io/api/#device-api-device-configure), [Device Monitor](https://docs.mender.io/api/#devices-api-device-monitor), remote console... -* Support new board and prove it is cross-platform and that it is able to work on small MCU too: STM32Lx, STM32F7, ATSAMD51... -* Integration of ATECC608A secure element to perform TLS operations. -* Support other RTOS and bare metal. +* Integration of other nice to have Mender APIs as new add-ons: [Device Configure](https://docs.mender.io/api/#device-api-device-configure), [Device Monitor](https://docs.mender.io/api/#devices-api-device-monitor), remote console... +* Support new board and prove it is cross-platform and that it is able to work on small MCU too: STM32F7, ATSAMD51... +* Integration of ATECC608B secure element to perform TLS authentication. +* Support other RTOS (particularly Azure RTOS) and bare metal platforms. * ... diff --git a/VERSION b/VERSION index 0ea3a94..0d91a54 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.0 +0.3.0 diff --git a/add-ons/src/mender-inventory.c b/add-ons/src/mender-inventory.c new file mode 100755 index 0000000..4103a75 --- /dev/null +++ b/add-ons/src/mender-inventory.c @@ -0,0 +1,232 @@ +/** + * @file mender-inventory.c + * @brief Mender MCU Inventory add-on implementation + * + * MIT License + * + * Copyright (c) 2022-2023 joelguittet and mender-mcu-client contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "mender-api.h" +#include "mender-inventory.h" +#include "mender-log.h" +#include "mender-rtos.h" + +#ifdef CONFIG_MENDER_CLIENT_ADD_ON_INVENTORY + +/** + * @brief Default inventory poll interval (seconds) + */ +#define MENDER_INVENTORY_DEFAULT_POLL_INTERVAL (28800) + +/** + * @brief Mender inventory configuration + */ +static mender_inventory_config_t mender_inventory_config; + +/** + * @brief Mender inventory + */ +static mender_inventory_t *mender_inventory = NULL; +static void * mender_inventory_mutex = NULL; + +/** + * @brief Mender inventory work handle + */ +static void *mender_inventory_work_handle = NULL; + +/** + * @brief Mender inventory work function + * @return MENDER_OK if the function succeeds, error code otherwise + */ +static mender_err_t mender_inventory_work_function(void); + +mender_err_t +mender_inventory_init(mender_inventory_config_t *config) { + + assert(NULL != config); + mender_err_t ret; + + /* Save configuration */ + if (NULL == (mender_inventory_config.artifact_name = strdup(config->artifact_name))) { + mender_log_error("Unable to save artifact name"); + return MENDER_FAIL; + } + if (NULL == (mender_inventory_config.device_type = strdup(config->device_type))) { + mender_log_error("Unable to save device type"); + return MENDER_FAIL; + } + if (0 != config->poll_interval) { + mender_inventory_config.poll_interval = config->poll_interval; + } else { + mender_inventory_config.poll_interval = MENDER_INVENTORY_DEFAULT_POLL_INTERVAL; + } + + /* Create inventory mutex */ + if (MENDER_OK != (ret = mender_rtos_mutex_create(&mender_inventory_mutex))) { + mender_log_error("Unable to create inventory mutex"); + return ret; + } + + /* Create mender inventory work */ + mender_rtos_work_params_t inventory_work_params; + inventory_work_params.function = mender_inventory_work_function; + inventory_work_params.period = mender_inventory_config.poll_interval; + inventory_work_params.name = "mender_inventory"; + if (MENDER_OK != (ret = mender_rtos_work_create(&inventory_work_params, &mender_inventory_work_handle))) { + mender_log_error("Unable to create inventory work"); + return ret; + } + + return MENDER_OK; +} + +mender_err_t +mender_inventory_activate(void) { + + mender_err_t ret; + + /* Activate inventory work */ + if (MENDER_OK != (ret = mender_rtos_work_activate(mender_inventory_work_handle))) { + mender_log_error("Unable to activate inventory work"); + return ret; + } + + return MENDER_OK; +} + +mender_err_t +mender_inventory_set(mender_inventory_t *inventory) { + + mender_err_t ret; + + /* Take mutex used to protect access to the inventory list */ + if (MENDER_OK != (ret = mender_rtos_mutex_take(mender_inventory_mutex, -1))) { + mender_log_error("Unable to take mutex"); + return ret; + } + + /* Release previous inventory */ + if (NULL != mender_inventory) { + size_t index = 0; + while ((NULL != mender_inventory[index].name) || (NULL != mender_inventory[index].value)) { + if (NULL != mender_inventory[index].name) { + free(mender_inventory[index].name); + } + if (NULL != mender_inventory[index].value) { + free(mender_inventory[index].value); + } + index++; + } + free(mender_inventory); + mender_inventory = NULL; + } + + /* Copy the new inventory */ + size_t inventory_length = 0; + if (NULL != inventory) { + while ((NULL != inventory[inventory_length].name) && (NULL != inventory[inventory_length].value)) { + inventory_length++; + } + } + if (NULL == (mender_inventory = (mender_inventory_t *)malloc((inventory_length + 1) * sizeof(mender_inventory_t)))) { + mender_log_error("Unable to allocate memory"); + mender_rtos_mutex_give(mender_inventory_mutex); + return MENDER_FAIL; + } + memset(mender_inventory, 0, (inventory_length + 1) * sizeof(mender_inventory_t)); + for (size_t index = 0; index < inventory_length; index++) { + if (NULL == (mender_inventory[index].name = strdup(inventory[index].name))) { + mender_log_error("Unable to allocate memory"); + } + if (NULL == (mender_inventory[index].value = strdup(inventory[index].value))) { + mender_log_error("Unable to allocate memory"); + } + } + + /* Release mutex used to protect access to the inventory list */ + mender_rtos_mutex_give(mender_inventory_mutex); + + return ret; +} + +mender_err_t +mender_inventory_exit(void) { + + /* Deactivate mender inventory work */ + mender_rtos_work_deactivate(mender_inventory_work_handle); + + /* Delete mender inventory work */ + mender_rtos_work_delete(mender_inventory_work_handle); + mender_inventory_work_handle = NULL; + + /* Release memory */ + if (NULL != mender_inventory_config.artifact_name) { + free(mender_inventory_config.artifact_name); + mender_inventory_config.artifact_name = NULL; + } + if (NULL != mender_inventory_config.device_type) { + free(mender_inventory_config.device_type); + mender_inventory_config.device_type = NULL; + } + mender_inventory_config.poll_interval = 0; + if (NULL != mender_inventory) { + size_t index = 0; + while ((NULL != mender_inventory[index].name) || (NULL != mender_inventory[index].value)) { + if (NULL != mender_inventory[index].name) { + free(mender_inventory[index].name); + } + if (NULL != mender_inventory[index].value) { + free(mender_inventory[index].value); + } + index++; + } + free(mender_inventory); + mender_inventory = NULL; + } + mender_rtos_mutex_delete(mender_inventory_mutex); + + return MENDER_OK; +} + +static mender_err_t +mender_inventory_work_function(void) { + + mender_err_t ret; + + /* Take mutex used to protect access to the inventory list */ + if (MENDER_OK != (ret = mender_rtos_mutex_take(mender_inventory_mutex, -1))) { + mender_log_error("Unable to take mutex"); + return ret; + } + + /* Publish inventory */ + if (MENDER_OK != (ret = mender_api_publish_inventory_data(mender_inventory))) { + mender_log_error("Unable to publish inventory data"); + } + + /* Release mutex used to protect access to the inventory list */ + mender_rtos_mutex_give(mender_inventory_mutex); + + return ret; +} + +#endif /* CONFIG_MENDER_CLIENT_ADD_ON_INVENTORY */ diff --git a/core/src/mender-api.c b/core/src/mender-api.c old mode 100644 new mode 100755 index 70f8844..d8dee91 --- a/core/src/mender-api.c +++ b/core/src/mender-api.c @@ -226,92 +226,6 @@ mender_api_perform_authentication(unsigned char *private_key, size_t private_key return ret; } -mender_err_t -mender_api_publish_inventory_data(mender_inventory_t *inventory, size_t inventory_length) { - - mender_err_t ret; - char * payload = NULL; - char * response = NULL; - int status = 0; - - /* Format payload */ - cJSON *array = cJSON_CreateArray(); - if (NULL == array) { - mender_log_error("Unable to allocate memory"); - ret = MENDER_FAIL; - goto END; - } - cJSON *item = cJSON_CreateObject(); - if (NULL == item) { - mender_log_error("Unable to allocate memory"); - ret = MENDER_FAIL; - goto END; - } - cJSON_AddStringToObject(item, "name", "artifact_name"); - cJSON_AddStringToObject(item, "value", mender_api_config.artifact_name); - cJSON_AddItemToArray(array, item); - item = cJSON_CreateObject(); - if (NULL == item) { - mender_log_error("Unable to allocate memory"); - ret = MENDER_FAIL; - goto END; - } - cJSON_AddStringToObject(item, "name", "device_type"); - cJSON_AddStringToObject(item, "value", mender_api_config.device_type); - cJSON_AddItemToArray(array, item); - if (NULL != inventory) { - for (size_t index = 0; index < inventory_length; index++) { - if (NULL == (item = cJSON_CreateObject())) { - mender_log_error("Unable to allocate memory"); - ret = MENDER_FAIL; - goto END; - } - cJSON_AddStringToObject(item, "name", inventory[index].name); - cJSON_AddStringToObject(item, "value", inventory[index].value); - cJSON_AddItemToArray(array, item); - } - } - payload = cJSON_Print(array); - - /* Perform HTTP request */ - if (MENDER_OK - != (ret = mender_http_perform(mender_api_jwt, - MENDER_API_PATH_PUT_DEVICE_ATTRIBUTES, - MENDER_HTTP_PUT, - payload, - NULL, - &mender_client_http_text_callback, - (void *)&response, - &status))) { - mender_log_error("Unable to perform HTTP request"); - goto END; - } - - /* Treatment depending of the status */ - if (200 == status) { - /* No response expected */ - ret = MENDER_OK; - } else { - mender_api_print_response_error(response, status); - ret = MENDER_FAIL; - } - -END: - - /* Release memory */ - if (NULL != response) { - free(response); - } - if (NULL != payload) { - free(payload); - } - if (NULL != array) { - cJSON_Delete(array); - } - - return ret; -} - mender_err_t mender_api_check_for_deployment(char **id, char **artifact_name, char **uri) { @@ -500,6 +414,98 @@ mender_api_download_artifact(char *uri) { return ret; } +#ifdef CONFIG_MENDER_CLIENT_ADD_ON_INVENTORY + +mender_err_t +mender_api_publish_inventory_data(mender_inventory_t *inventory) { + + mender_err_t ret; + char * payload = NULL; + char * response = NULL; + int status = 0; + + /* Format payload */ + cJSON *array = cJSON_CreateArray(); + if (NULL == array) { + mender_log_error("Unable to allocate memory"); + ret = MENDER_FAIL; + goto END; + } + cJSON *item = cJSON_CreateObject(); + if (NULL == item) { + mender_log_error("Unable to allocate memory"); + ret = MENDER_FAIL; + goto END; + } + cJSON_AddStringToObject(item, "name", "artifact_name"); + cJSON_AddStringToObject(item, "value", mender_api_config.artifact_name); + cJSON_AddItemToArray(array, item); + item = cJSON_CreateObject(); + if (NULL == item) { + mender_log_error("Unable to allocate memory"); + ret = MENDER_FAIL; + goto END; + } + cJSON_AddStringToObject(item, "name", "device_type"); + cJSON_AddStringToObject(item, "value", mender_api_config.device_type); + cJSON_AddItemToArray(array, item); + if (NULL != inventory) { + size_t index = 0; + while ((NULL != inventory[index].name) && (NULL != inventory[index].value)) { + if (NULL == (item = cJSON_CreateObject())) { + mender_log_error("Unable to allocate memory"); + ret = MENDER_FAIL; + goto END; + } + cJSON_AddStringToObject(item, "name", inventory[index].name); + cJSON_AddStringToObject(item, "value", inventory[index].value); + cJSON_AddItemToArray(array, item); + index++; + } + } + payload = cJSON_Print(array); + + /* Perform HTTP request */ + if (MENDER_OK + != (ret = mender_http_perform(mender_api_jwt, + MENDER_API_PATH_PUT_DEVICE_ATTRIBUTES, + MENDER_HTTP_PUT, + payload, + NULL, + &mender_client_http_text_callback, + (void *)&response, + &status))) { + mender_log_error("Unable to perform HTTP request"); + goto END; + } + + /* Treatment depending of the status */ + if (200 == status) { + /* No response expected */ + ret = MENDER_OK; + } else { + mender_api_print_response_error(response, status); + ret = MENDER_FAIL; + } + +END: + + /* Release memory */ + if (NULL != response) { + free(response); + } + if (NULL != payload) { + free(payload); + } + if (NULL != array) { + cJSON_Delete(array); + } + + return ret; +} + +#endif /* CONFIG_MENDER_CLIENT_ADD_ON_INVENTORY */ + mender_err_t mender_api_exit(void) { diff --git a/core/src/mender-client.c b/core/src/mender-client.c old mode 100644 new mode 100755 index 1c1e84b..4a0df34 --- a/core/src/mender-client.c +++ b/core/src/mender-client.c @@ -41,37 +41,13 @@ /** * @brief Default authentication poll interval (seconds) */ -#define MENDER_CLIENT_DEFAULT_AUTHENTICATION_POLL_INTERVAL (60) - -/** - * @brief Default inventory poll interval (seconds) - */ -#define MENDER_CLIENT_DEFAULT_INVENTORY_POLL_INTERVAL (28800) +#define MENDER_CLIENT_DEFAULT_AUTHENTICATION_POLL_INTERVAL (600) /** * @brief Default update poll interval (seconds) */ #define MENDER_CLIENT_DEFAULT_UPDATE_POLL_INTERVAL (1800) -/** - * @brief Default restart poll interval (seconds) - */ -#define MENDER_CLIENT_DEFAULT_RESTART_POLL_INTERVAL (60) - -/** - * @brief Mender client task size configuration (10% margin included) - */ -#ifndef MENDER_CLIENT_TASK_STACK_SIZE -#define MENDER_CLIENT_TASK_STACK_SIZE (14 * 1024) -#endif - -/** - * @brief Mender client task priority configuration - */ -#ifndef MENDER_CLIENT_TASK_PRIORITY -#define MENDER_CLIENT_TASK_PRIORITY (5) -#endif - /** * @brief Mender client configuration */ @@ -97,22 +73,29 @@ static char *mender_client_ota_id = NULL; static char *mender_client_ota_artifact_name = NULL; /** - * @brief Mender client inventory + * @brief Mender client work handles + */ +static void *mender_client_initialization_work_handle = NULL; +static void *mender_client_authentication_work_handle = NULL; +static void *mender_client_update_work_handle = NULL; + +/** + * @brief Mender client initialization work function + * @return MENDER_OK if the function succeeds, error code otherwise */ -static mender_inventory_t *mender_client_inventory = NULL; -static size_t mender_client_inventory_length = 0; -static void * mender_client_inventory_mutex = NULL; +static mender_err_t mender_client_initialization_work_function(void); /** - * @brief Mender client task handle + * @brief Mender client authentication work function + * @return MENDER_OK if the function succeeds, error code otherwise */ -static void *mender_client_task_handle = NULL; +static mender_err_t mender_client_authentication_work_function(void); /** - * @brief Mender client task - * @param arg Argument + * @brief Mender client update work function + * @return MENDER_OK if the function succeeds, error code otherwise */ -static void mender_client_task(void *arg); +static mender_err_t mender_client_update_work_function(void); /** * @brief Publish deployment status of the device to the mender-server and invoke deployment status callback @@ -175,27 +158,21 @@ mender_client_init(mender_client_config_t *config, mender_client_callbacks_t *ca } else { mender_client_config.authentication_poll_interval = MENDER_CLIENT_DEFAULT_AUTHENTICATION_POLL_INTERVAL; } - if (0 != config->inventory_poll_interval) { - mender_client_config.inventory_poll_interval = config->inventory_poll_interval; - } else { - mender_client_config.inventory_poll_interval = MENDER_CLIENT_DEFAULT_INVENTORY_POLL_INTERVAL; - } if (0 != config->update_poll_interval) { mender_client_config.update_poll_interval = config->update_poll_interval; } else { mender_client_config.update_poll_interval = MENDER_CLIENT_DEFAULT_UPDATE_POLL_INTERVAL; } - if (0 != config->restart_poll_interval) { - mender_client_config.restart_poll_interval = config->restart_poll_interval; - } else { - mender_client_config.restart_poll_interval = MENDER_CLIENT_DEFAULT_RESTART_POLL_INTERVAL; - } mender_client_config.recommissioning = config->recommissioning; /* Save callbacks */ memcpy(&mender_client_callbacks, callbacks, sizeof(mender_client_callbacks_t)); /* Initializations */ + if (MENDER_OK != (ret = mender_rtos_init())) { + mender_log_error("Unable to initialize rtos"); + return ret; + } if (MENDER_OK != (ret = mender_log_init())) { mender_log_error("Unable to initialize log"); return ret; @@ -226,55 +203,37 @@ mender_client_init(mender_client_config_t *config, mender_client_callbacks_t *ca return ret; } - /* Create inventory mutex */ - if (NULL == (mender_client_inventory_mutex = mender_rtos_semaphore_create_mutex())) { - mender_log_error("Unable to create inventory mutex"); - return MENDER_FAIL; - } - - /* Create mender client task */ - mender_rtos_task_create(mender_client_task, "mender_client", MENDER_CLIENT_TASK_STACK_SIZE, NULL, MENDER_CLIENT_TASK_PRIORITY, &mender_client_task_handle); - - return MENDER_OK; -} - -mender_err_t -mender_client_set_inventory(mender_inventory_t *inventory, size_t inventory_length) { - - mender_rtos_semaphore_take(mender_client_inventory_mutex, -1); - - /* Release previous inventory */ - if (NULL != mender_client_inventory) { - for (size_t index = 0; index < mender_client_inventory_length; index++) { - if (NULL != mender_client_inventory[index].name) { - free(mender_client_inventory[index].name); - } - if (NULL != mender_client_inventory[index].value) { - free(mender_client_inventory[index].value); - } - } - free(mender_client_inventory); - mender_client_inventory = NULL; + /* Create mender client works */ + mender_rtos_work_params_t initialization_work_params; + initialization_work_params.function = mender_client_initialization_work_function; + initialization_work_params.period = mender_client_config.authentication_poll_interval; + initialization_work_params.name = "mender_client_initialization"; + if (MENDER_OK != (ret = mender_rtos_work_create(&initialization_work_params, &mender_client_initialization_work_handle))) { + mender_log_error("Unable to create initialization work"); + return ret; } - - /* Copy the new inventory */ - if (NULL == (mender_client_inventory = (mender_inventory_t *)malloc(inventory_length * sizeof(mender_inventory_t)))) { - mender_log_error("Unable to allocate memory"); - mender_rtos_semaphore_give(mender_client_inventory_mutex); - return MENDER_FAIL; + mender_rtos_work_params_t authentication_work_params; + authentication_work_params.function = mender_client_authentication_work_function; + authentication_work_params.period = mender_client_config.authentication_poll_interval; + authentication_work_params.name = "mender_client_authentication"; + if (MENDER_OK != (ret = mender_rtos_work_create(&authentication_work_params, &mender_client_authentication_work_handle))) { + mender_log_error("Unable to create authentication work"); + return ret; } - memset(mender_client_inventory, 0, inventory_length * sizeof(mender_inventory_t)); - for (size_t index = 0; index < inventory_length; index++) { - if (NULL == (mender_client_inventory[index].name = strdup(inventory[index].name))) { - mender_log_error("Unable to allocate memory"); - } - if (NULL == (mender_client_inventory[index].value = strdup(inventory[index].value))) { - mender_log_error("Unable to allocate memory"); - } + mender_rtos_work_params_t update_work_params; + update_work_params.function = mender_client_update_work_function; + update_work_params.period = mender_client_config.update_poll_interval; + update_work_params.name = "mender_client_update"; + if (MENDER_OK != (ret = mender_rtos_work_create(&update_work_params, &mender_client_update_work_handle))) { + mender_log_error("Unable to create update work"); + return ret; } - mender_client_inventory_length = inventory_length; - mender_rtos_semaphore_give(mender_client_inventory_mutex); + /* Activate initialization work */ + if (MENDER_OK != (ret = mender_rtos_work_activate(mender_client_initialization_work_handle))) { + mender_log_error("Unable to activate initialization work"); + return ret; + } return MENDER_OK; } @@ -282,15 +241,25 @@ mender_client_set_inventory(mender_inventory_t *inventory, size_t inventory_leng mender_err_t mender_client_exit(void) { - /* Stop mender-client task */ - mender_rtos_task_delete(mender_client_task_handle); - mender_client_task_handle = NULL; + /* Deactivate mender client works */ + mender_rtos_work_deactivate(mender_client_initialization_work_handle); + mender_rtos_work_deactivate(mender_client_authentication_work_handle); + mender_rtos_work_deactivate(mender_client_update_work_handle); + + /* Delete mender client works */ + mender_rtos_work_delete(mender_client_initialization_work_handle); + mender_client_initialization_work_handle = NULL; + mender_rtos_work_delete(mender_client_authentication_work_handle); + mender_client_authentication_work_handle = NULL; + mender_rtos_work_delete(mender_client_update_work_handle); + mender_client_update_work_handle = NULL; /* Release all modules */ mender_api_exit(); mender_tls_exit(); mender_storage_exit(); mender_log_exit(); + mender_rtos_exit(); /* Release memory */ if (NULL != mender_client_config.mac_address) { @@ -314,9 +283,7 @@ mender_client_exit(void) { mender_client_config.tenant_token = NULL; } mender_client_config.authentication_poll_interval = 0; - mender_client_config.inventory_poll_interval = 0; mender_client_config.update_poll_interval = 0; - mender_client_config.restart_poll_interval = 0; if (NULL != mender_client_private_key) { free(mender_client_private_key); mender_client_private_key = NULL; @@ -335,28 +302,14 @@ mender_client_exit(void) { free(mender_client_ota_artifact_name); mender_client_ota_artifact_name = NULL; } - if (NULL != mender_client_inventory) { - for (size_t index = 0; index < mender_client_inventory_length; index++) { - if (NULL != mender_client_inventory[index].name) { - free(mender_client_inventory[index].name); - } - if (NULL != mender_client_inventory[index].value) { - free(mender_client_inventory[index].value); - } - } - free(mender_client_inventory); - mender_client_inventory = NULL; - } - mender_client_inventory_length = 0; - mender_rtos_semaphore_delete(mender_client_inventory_mutex); return MENDER_OK; } -static void -mender_client_task(void *arg) { +static mender_err_t +mender_client_initialization_work_function(void) { - (void)arg; + mender_err_t ret; /* Check if recommissioning is forced */ if (true == mender_client_config.recommissioning) { @@ -364,7 +317,7 @@ mender_client_task(void *arg) { /* Erase authentication keys */ mender_log_info("Erasing authentication keys..."); if (MENDER_OK != mender_storage_erase_authentication_keys()) { - mender_log_error("Unable to erase authentication keys"); + mender_log_warning("Unable to erase authentication keys"); } } @@ -376,204 +329,204 @@ mender_client_task(void *arg) { /* Generate authentication keys */ mender_log_info("Generating authentication keys..."); if (MENDER_OK - != mender_tls_generate_authentication_keys( - &mender_client_private_key, &mender_client_private_key_length, &mender_client_public_key, &mender_client_public_key_length)) { + != (ret = mender_tls_generate_authentication_keys( + &mender_client_private_key, &mender_client_private_key_length, &mender_client_public_key, &mender_client_public_key_length))) { mender_log_error("Unable to generate authentication keys"); - goto END; + return ret; } /* Record keys */ if (MENDER_OK - != mender_storage_set_authentication_keys( - mender_client_private_key, mender_client_private_key_length, mender_client_public_key, mender_client_public_key_length)) { + != (ret = mender_storage_set_authentication_keys( + mender_client_private_key, mender_client_private_key_length, mender_client_public_key, mender_client_public_key_length))) { mender_log_error("Unable to record authentication keys"); - goto END; + return ret; } } /* Retrieve OTA ID if it is found (following an update) */ - mender_err_t ret; - size_t ota_id_length = 0; - size_t ota_artifact_name_length = 0; + size_t ota_id_length = 0; + size_t ota_artifact_name_length = 0; if (MENDER_OK != (ret = mender_storage_get_ota_deployment(&mender_client_ota_id, &ota_id_length, &mender_client_ota_artifact_name, &ota_artifact_name_length))) { if (MENDER_NOT_FOUND != ret) { mender_log_error("Unable to get OTA ID"); - goto END; + return ret; } } - /* Loop until authentication is done or rebooting is required */ - unsigned long last_wake_time = 0; - mender_rtos_delay_until_init(&last_wake_time); - while (true) { + /* Activate authentication work */ + if (MENDER_OK != (ret = mender_rtos_work_activate(mender_client_authentication_work_handle))) { + mender_log_error("Unable to activate authentication work"); + return ret; + } - /* Perform authentication with the mender server */ - if (MENDER_OK - != mender_api_perform_authentication( - mender_client_private_key, mender_client_private_key_length, mender_client_public_key, mender_client_public_key_length)) { - - /* Invoke authentication error callback */ - if (NULL != mender_client_callbacks.authentication_failure) { - if (MENDER_OK != mender_client_callbacks.authentication_failure()) { - - /* Check if OTA is pending */ - if ((NULL != mender_client_ota_id) && (NULL != mender_client_ota_artifact_name)) { - /* Authentication error callback inform the reboot should be done, probably something is broken and it prefers to rollback */ - mender_log_error("Authentication error callback failed, rebooting"); - goto REBOOT; - } + return MENDER_DONE; +} + +static mender_err_t +mender_client_authentication_work_function(void) { + + mender_err_t ret; + + /* Perform authentication with the mender server */ + if (MENDER_OK + != (ret = mender_api_perform_authentication( + mender_client_private_key, mender_client_private_key_length, mender_client_public_key, mender_client_public_key_length))) { + + /* Invoke authentication error callback */ + if (NULL != mender_client_callbacks.authentication_failure) { + if (MENDER_OK != mender_client_callbacks.authentication_failure()) { + + /* Check if OTA is pending */ + if ((NULL != mender_client_ota_id) && (NULL != mender_client_ota_artifact_name)) { + /* Authentication error callback inform the reboot should be done, probably something is broken and it prefers to rollback */ + mender_log_error("Authentication error callback failed, rebooting"); + goto REBOOT; } } + } - } else { + return ret; + } + + /* Invoke authentication success callback */ + if (NULL != mender_client_callbacks.authentication_success) { + if (MENDER_OK != mender_client_callbacks.authentication_success()) { + + /* Check if OTA is pending */ + if ((NULL != mender_client_ota_id) && (NULL != mender_client_ota_artifact_name)) { + /* Authentication success callback inform the reboot should be done, probably something is broken and it prefers to rollback */ + mender_log_error("Authentication success callback failed, rebooting"); + goto REBOOT; + } + } + } - /* Invoke authentication success callback */ - if (NULL != mender_client_callbacks.authentication_success) { - if (MENDER_OK != mender_client_callbacks.authentication_success()) { + /* Check if OTA is pending */ + if ((NULL != mender_client_ota_id) && (NULL != mender_client_ota_artifact_name)) { - /* Check if OTA is pending */ - if ((NULL != mender_client_ota_id) && (NULL != mender_client_ota_artifact_name)) { - /* Authentication sucess callback inform the reboot should be done, probably something is broken and it prefers to rollback */ - mender_log_error("Authentication success callback failed, rebooting"); - goto REBOOT; - } + /* Check if artifact running is the pending one, fails if rollback occurred */ + if ((NULL != mender_client_ota_artifact_name) && (strcmp(mender_client_ota_artifact_name, mender_client_config.artifact_name))) { - } else { + /* Publish deployment status failure */ + mender_client_publish_deployment_status(mender_client_ota_id, MENDER_DEPLOYMENT_STATUS_FAILURE); - /* Check if OTA is pending */ - if ((NULL != mender_client_ota_id) && (NULL != mender_client_ota_artifact_name)) { + } else { - /* Check if artifact running is the pending one, fails if rollback occurred */ - if ((NULL != mender_client_ota_artifact_name) && (strcmp(mender_client_ota_artifact_name, mender_client_config.artifact_name))) { + /* Publish deployment status success */ + mender_client_publish_deployment_status(mender_client_ota_id, MENDER_DEPLOYMENT_STATUS_SUCCESS); + } - /* Publish deployment status failure */ - mender_client_publish_deployment_status(mender_client_ota_id, MENDER_DEPLOYMENT_STATUS_FAILURE); + /* Clear pending OTA */ + mender_storage_clear_ota_deployment(); + } - } else { + /* Activate update work */ + if (MENDER_OK != (ret = mender_rtos_work_activate(mender_client_update_work_handle))) { + mender_log_error("Unable to activate update work"); + return ret; + } - /* Publish deployment status success */ - mender_client_publish_deployment_status(mender_client_ota_id, MENDER_DEPLOYMENT_STATUS_SUCCESS); - } + return MENDER_DONE; - /* Clear pending OTA */ - mender_storage_clear_ota_deployment(); - } - } - } - break; - } +REBOOT: - /* Wait before trying again */ - mender_rtos_delay_until_s(&last_wake_time, mender_client_config.authentication_poll_interval); + /* Invoke restart callback, application is responsible to shutdown properly and restart the system */ + if (NULL != mender_client_callbacks.restart) { + mender_client_callbacks.restart(); } - /* Loop until rebooting is required */ - uint32_t inventory_timeout = 0; - uint32_t check_for_deployment_timeout = 0; - mender_rtos_delay_until_init(&last_wake_time); - while (true) { + return MENDER_DONE; +} - /* Check if it's time to send inventory */ - if (0 == inventory_timeout) { +static mender_err_t +mender_client_update_work_function(void) { - /* Publish inventory */ - mender_rtos_semaphore_take(mender_client_inventory_mutex, -1); - if (MENDER_OK != mender_api_publish_inventory_data(mender_client_inventory, mender_client_inventory_length)) { - mender_log_error("Unable to publish inventory data"); - } - mender_rtos_semaphore_give(mender_client_inventory_mutex); + mender_err_t ret; - /* Reload timeout */ - inventory_timeout = mender_client_config.inventory_poll_interval; - } + /* Check for deployment */ + char *id = NULL; + char *artifact_name = NULL; + char *uri = NULL; + if (MENDER_OK != (ret = mender_api_check_for_deployment(&id, &artifact_name, &uri))) { + mender_log_error("Unable to check for deployment"); + goto END; + } - /* Check if it's time to check for deployment */ - if (0 == check_for_deployment_timeout) { - - /* Check for deployment */ - char *id = NULL; - char *artifact_name = NULL; - char *uri = NULL; - if (MENDER_OK != mender_api_check_for_deployment(&id, &artifact_name, &uri)) { - mender_log_error("Unable to check for deployment"); - } else if ((NULL != id) && (NULL != artifact_name) && (NULL != uri)) { - /* Check if artifact is already installed (should not occur) */ - if (!strcmp(artifact_name, mender_client_config.artifact_name)) { - mender_log_error("Artifact is already installed"); - mender_client_publish_deployment_status(id, MENDER_DEPLOYMENT_STATUS_FAILURE); - } else { - /* Download deployment artifact */ - mender_log_info("Downloading deployment artifact with id '%s', artifact name '%s' and uri '%s'", id, artifact_name, uri); - mender_client_publish_deployment_status(id, MENDER_DEPLOYMENT_STATUS_DOWNLOADING); - if (MENDER_OK != mender_api_download_artifact(uri)) { - mender_log_error("Unable to download artifact"); - mender_client_publish_deployment_status(id, MENDER_DEPLOYMENT_STATUS_FAILURE); - } else { - mender_log_info("Download done, installing artifact"); - mender_client_publish_deployment_status(id, MENDER_DEPLOYMENT_STATUS_INSTALLING); - /* Set boot partition */ - if (MENDER_OK != mender_client_callbacks.ota_set_boot_partition()) { - mender_log_error("Unable to set boot partition"); - mender_client_publish_deployment_status(id, MENDER_DEPLOYMENT_STATUS_FAILURE); - } else { - /* Save OTA ID to publish deployment status after rebooting */ - if (MENDER_OK != mender_storage_set_ota_deployment(id, artifact_name)) { - mender_log_error("Unable to save OTA ID"); - mender_client_publish_deployment_status(id, MENDER_DEPLOYMENT_STATUS_FAILURE); - } else { - /* Now need to reboot to apply the update */ - mender_client_publish_deployment_status(id, MENDER_DEPLOYMENT_STATUS_REBOOTING); - goto REBOOT; - } - } - } - } - } else { - mender_log_info("No deployment available"); - } + /* Check if deployment is available */ + if ((NULL == id) || (NULL == artifact_name) || (NULL == uri)) { + mender_log_info("No deployment available"); + goto END; + } - /* Release memory */ - if (NULL != id) { - free(id); - } - if (NULL != artifact_name) { - free(artifact_name); - } - if (NULL != uri) { - free(uri); - } + /* Check if artifact is already installed (should not occur) */ + if (!strcmp(artifact_name, mender_client_config.artifact_name)) { + mender_log_error("Artifact is already installed"); + mender_client_publish_deployment_status(id, MENDER_DEPLOYMENT_STATUS_ALREADY_INSTALLED); + goto END; + } - /* Reload timeout */ - check_for_deployment_timeout = mender_client_config.update_poll_interval; - } + /* Download deployment artifact */ + mender_log_info("Downloading deployment artifact with id '%s', artifact name '%s' and uri '%s'", id, artifact_name, uri); + mender_client_publish_deployment_status(id, MENDER_DEPLOYMENT_STATUS_DOWNLOADING); + if (MENDER_OK != (ret = mender_api_download_artifact(uri))) { + mender_log_error("Unable to download artifact"); + mender_client_publish_deployment_status(id, MENDER_DEPLOYMENT_STATUS_FAILURE); + goto END; + } - /* Management of timeouts */ - uint32_t delay = (check_for_deployment_timeout <= inventory_timeout) ? check_for_deployment_timeout : inventory_timeout; - mender_rtos_delay_until_s(&last_wake_time, delay); - check_for_deployment_timeout -= delay; - inventory_timeout -= delay; + /* Set boot partition */ + mender_log_info("Download done, installing artifact"); + mender_client_publish_deployment_status(id, MENDER_DEPLOYMENT_STATUS_INSTALLING); + if (MENDER_OK != (ret = mender_client_callbacks.ota_set_boot_partition())) { + mender_log_error("Unable to set boot partition"); + mender_client_publish_deployment_status(id, MENDER_DEPLOYMENT_STATUS_FAILURE); + goto END; } -REBOOT: + /* Save OTA ID to publish deployment status after rebooting */ + if (MENDER_OK != (ret = mender_storage_set_ota_deployment(id, artifact_name))) { + mender_log_error("Unable to save OTA ID"); + mender_client_publish_deployment_status(id, MENDER_DEPLOYMENT_STATUS_FAILURE); + goto END; + } - /* Infinite loop, when reaching this point this means you are waiting to reboot */ - mender_rtos_delay_until_init(&last_wake_time); - while (true) { + /* Now need to reboot to apply the update */ + mender_client_publish_deployment_status(id, MENDER_DEPLOYMENT_STATUS_REBOOTING); - /* Invoke restart callback */ - if (NULL != mender_client_callbacks.restart) { - mender_client_callbacks.restart(); - } + /* Release memory */ + if (NULL != id) { + free(id); + } + if (NULL != artifact_name) { + free(artifact_name); + } + if (NULL != uri) { + free(uri); + } - /* Wait before trying again */ - mender_rtos_delay_until_s(&last_wake_time, mender_client_config.restart_poll_interval); + /* Invoke restart callback, application is responsible to shutdown properly and restart the system */ + if (NULL != mender_client_callbacks.restart) { + mender_client_callbacks.restart(); } + return MENDER_DONE; + END: - /* Delete myself */ - mender_rtos_task_delete(NULL); + /* Release memory */ + if (NULL != id) { + free(id); + } + if (NULL != artifact_name) { + free(artifact_name); + } + if (NULL != uri) { + free(uri); + } + + return ret; } static mender_err_t diff --git a/esp-idf/CMakeLists.txt b/esp-idf/CMakeLists.txt new file mode 100755 index 0000000..b01ce0e --- /dev/null +++ b/esp-idf/CMakeLists.txt @@ -0,0 +1,50 @@ +# @file CMakeLists.txt +# @brief mender-mcu-client esp-idf component CMakeLists file +# +# MIT License +# +# Copyright (c) 2022-2023 joelguittet and mender-mcu-client contributors +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# List of sources +list(APPEND srcs + "${CMAKE_CURRENT_LIST_DIR}/../core/src/mender-api.c" + "${CMAKE_CURRENT_LIST_DIR}/../core/src/mender-client.c" + "${CMAKE_CURRENT_LIST_DIR}/../core/src/mender-untar.c" + "${CMAKE_CURRENT_LIST_DIR}/../core/src/mender-utils.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/board/esp-idf/src/mender-log.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/board/esp-idf/src/mender-ota.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/board/esp-idf/src/mender-storage.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/http/esp-idf/src/mender-http.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/rtos/freertos/src/mender-rtos.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/tls/mbedtls/src/mender-tls.c" +) +if(CONFIG_MENDER_CLIENT_ADD_ON_INVENTORY) + list(APPEND srcs + "${CMAKE_CURRENT_LIST_DIR}/../add-ons/src/mender-inventory.c" + ) +endif() + +# Register mender-mcu-client component +idf_component_register( + SRCS ${srcs} + INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/../include" + REQUIRES app_update esp_http_client json mbedtls nvs_flash +) diff --git a/esp-idf/Kconfig b/esp-idf/Kconfig new file mode 100755 index 0000000..80e6c6c --- /dev/null +++ b/esp-idf/Kconfig @@ -0,0 +1,71 @@ +# @file Kconfig +# @brief ESP-IDF Kconfig +# +# MIT License +# +# Copyright (c) 2022-2023 joelguittet and mender-mcu-client contributors +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +menu "Mender client Configuration" + + config MENDER_SERVER_HOST + string "Mender server host URL" + default "https://hosted.mender.io" + help + Set the Mender server host URL to be used on the device. + + config MENDER_SERVER_TENANT_TOKEN + string "Mender server Tenant Token" + help + Set the Mender server Tenant Token, to be used with https://hosted.mender.io. + + config MENDER_CLIENT_AUTHENTICATION_POLL_INTERVAL + int "Mender client Authentication poll interval (seconds)" + range 0 3600 + default 600 + help + Interval used to periodically try to authenticate to the mender server until it succeeds. + + config MENDER_CLIENT_UPDATE_POLL_INTERVAL + int "Mender client Update poll interval (seconds)" + range 0 86400 + default 1800 + help + Interval used to periodically check for new deployments on the mender server. + + config MENDER_CLIENT_ADD_ON_INVENTORY + bool "Mender client Inventory" + default y + help + Inventory add-ons permits to send inventory key-value pairs to the Mender server. + It is particurlarly used to send artifact name and device type, and it permits to see the last check-in time of the device. + + if MENDER_CLIENT_ADD_ON_INVENTORY + + config MENDER_CLIENT_INVENTORY_POLL_INTERVAL + int "Mender client Inventory poll interval (seconds)" + range 0 86400 + default 28800 + help + Interval used to periodically send inventory to the mender server. + + endif + +endmenu diff --git a/include/mender-api.h b/include/mender-api.h old mode 100644 new mode 100755 index f8fa347..2d5f62e --- a/include/mender-api.h +++ b/include/mender-api.h @@ -30,9 +30,10 @@ #ifdef __cplusplus extern "C" { -#endif +#endif /* __cplusplus */ #include "mender-common.h" +#include "mender-inventory.h" /** * @brief Mender API configuration @@ -73,14 +74,6 @@ mender_err_t mender_api_init(mender_api_config_t *config, mender_api_callbacks_t */ mender_err_t mender_api_perform_authentication(unsigned char *private_key, size_t private_key_length, unsigned char *public_key, size_t public_key_length); -/** - * @brief Publish inventory data of the device to the mender-server - * @param inventory Inventory array, NULL if not defined - * @param inventory_length Length of the inventory array, 0 if empty - * @return MENDER_OK if the function succeeds, error code otherwise - */ -mender_err_t mender_api_publish_inventory_data(mender_inventory_t *inventory, size_t inventory_length); - /** * @brief Check for deployments for the device from the mender-server * @param id ID of the deployment, if one is pending @@ -105,6 +98,17 @@ mender_err_t mender_api_publish_deployment_status(char *id, mender_deployment_st */ mender_err_t mender_api_download_artifact(char *uri); +#ifdef CONFIG_MENDER_CLIENT_ADD_ON_INVENTORY + +/** + * @brief Publish inventory data of the device to the mender-server + * @param inventory Mender inventory key/value pairs table, must end with a NULL/NULL element, NULL if not defined + * @return MENDER_OK if the function succeeds, error code otherwise + */ +mender_err_t mender_api_publish_inventory_data(mender_inventory_t *inventory); + +#endif /* CONFIG_MENDER_CLIENT_ADD_ON_INVENTORY */ + /** * @brief Release mender API * @return MENDER_OK if the function succeeds, error code otherwise @@ -113,6 +117,6 @@ mender_err_t mender_api_exit(void); #ifdef __cplusplus } -#endif +#endif /* __cplusplus */ #endif /* __MENDER_API_H__ */ diff --git a/include/mender-client.h b/include/mender-client.h old mode 100644 new mode 100755 index 99c5fce..3a4070f --- a/include/mender-client.h +++ b/include/mender-client.h @@ -30,7 +30,7 @@ #ifdef __cplusplus extern "C" { -#endif +#endif /* __cplusplus */ #include "mender-common.h" @@ -44,9 +44,7 @@ typedef struct { char * host; /**< URL of the mender server */ char * tenant_token; /**< Tenant token used to authenticate on the mender server (optional) */ uint32_t authentication_poll_interval; /**< Authentication poll interval, default is 60 seconds */ - uint32_t inventory_poll_interval; /**< Inventory poll interval, default is 28800 seconds */ uint32_t update_poll_interval; /**< Update poll interval, default is 1800 seconds */ - uint32_t restart_poll_interval; /**< Restart poll interval, default is 60 seconds */ bool recommissioning; /**< Used to force creation of new authentication keys */ } mender_client_config_t; @@ -73,14 +71,6 @@ typedef struct { */ mender_err_t mender_client_init(mender_client_config_t *config, mender_client_callbacks_t *callbacks); -/** - * @brief Set mender client inventory - * @param inventory Mender client inventory - * @param inventory_length Mender client inventory length - * @return MENDER_OK if the function succeeds, error code otherwise - */ -mender_err_t mender_client_set_inventory(mender_inventory_t *inventory, size_t inventory_length); - /** * @brief Release mender client * @return MENDER_OK if the function succeeds, error code otherwise @@ -89,6 +79,6 @@ mender_err_t mender_client_exit(void); #ifdef __cplusplus } -#endif +#endif /* __cplusplus */ #endif /* __MENDER_CLIENT_H__ */ diff --git a/include/mender-common.h b/include/mender-common.h old mode 100644 new mode 100755 index 87206cb..9a51649 --- a/include/mender-common.h +++ b/include/mender-common.h @@ -30,7 +30,7 @@ #ifdef __cplusplus extern "C" { -#endif +#endif /* __cplusplus */ #include #include @@ -44,6 +44,7 @@ extern "C" { * @brief Mender error codes */ typedef enum { + MENDER_DONE = 1, /**< Done */ MENDER_OK = 0, /**< OK */ MENDER_FAIL = -1, /**< Failure */ MENDER_NOT_FOUND = -2, /**< Not found */ @@ -61,16 +62,8 @@ typedef enum { MENDER_DEPLOYMENT_STATUS_ALREADY_INSTALLED /**< Status is "already installed" */ } mender_deployment_status_t; -/** - * @brief Inventory item - */ -typedef struct { - char *name; /**< Name of the item */ - char *value; /**< Value of the item */ -} mender_inventory_t; - #ifdef __cplusplus } -#endif +#endif /* __cplusplus */ #endif /* __MENDER_COMMON_H__ */ diff --git a/include/mender-http.h b/include/mender-http.h old mode 100644 new mode 100755 index 256c59a..eec0c45 --- a/include/mender-http.h +++ b/include/mender-http.h @@ -30,7 +30,7 @@ #ifdef __cplusplus extern "C" { -#endif +#endif /* __cplusplus */ #include "mender-common.h" @@ -97,6 +97,6 @@ mender_err_t mender_http_exit(void); #ifdef __cplusplus } -#endif +#endif /* __cplusplus */ #endif /* __MENDER_HTTP_H__ */ diff --git a/include/mender-inventory.h b/include/mender-inventory.h new file mode 100755 index 0000000..730eb3c --- /dev/null +++ b/include/mender-inventory.h @@ -0,0 +1,88 @@ +/** + * @file mender-inventory.h + * @brief Mender MCU Inventory add-on implementation + * + * MIT License + * + * Copyright (c) 2022-2023 joelguittet and mender-mcu-client contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __MENDER_INVENTORY_H__ +#define __MENDER_INVENTORY_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "mender-common.h" + +#ifdef CONFIG_MENDER_CLIENT_ADD_ON_INVENTORY + +/** + * @brief Inventory item + */ +typedef struct { + char *name; /**< Name of the item */ + char *value; /**< Value of the item */ +} mender_inventory_t; + +/** + * @brief Mender inventory configuration + */ +typedef struct { + char * artifact_name; /**< Artifact name */ + char * device_type; /**< Device type */ + uint32_t poll_interval; /**< Inventory poll interval, default is 28800 seconds */ +} mender_inventory_config_t; + +/** + * @brief Initialize mender inventory add-on + * @param config Mender inventory configuration + * @return MENDER_OK if the function succeeds, error code otherwise + */ +mender_err_t mender_inventory_init(mender_inventory_config_t *config); + +/** + * @brief Activate mender inventory add-on + * @return MENDER_OK if the function succeeds, error code otherwise + */ +mender_err_t mender_inventory_activate(void); + +/** + * @brief Set mender inventory + * @param inventory Mender inventory key/value pairs table, must end with a NULL/NULL element, NULL if not defined + * @return MENDER_OK if the function succeeds, error code otherwise + */ +mender_err_t mender_inventory_set(mender_inventory_t *inventory); + +/** + * @brief Release mender inventory add-on + * @return MENDER_OK if the function succeeds, error code otherwise + */ +mender_err_t mender_inventory_exit(void); + +#endif /* CONFIG_MENDER_CLIENT_ADD_ON_INVENTORY */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __MENDER_INVENTORY_H__ */ diff --git a/include/mender-log.h b/include/mender-log.h old mode 100644 new mode 100755 index 0017865..e5b3ce8 --- a/include/mender-log.h +++ b/include/mender-log.h @@ -30,7 +30,7 @@ #ifdef __cplusplus extern "C" { -#endif +#endif /* __cplusplus */ #include "mender-common.h" @@ -93,7 +93,7 @@ mender_err_t mender_log_print(mender_log_level_t level, const char *filename, co #define mender_log_debug(...) ({ mender_log_print(MENDER_LOG_DEBUG, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__); }) #else #define mender_log_debug(...) -#endif +#endif /* DEBUG */ /** * @brief Release mender log @@ -103,6 +103,6 @@ mender_err_t mender_log_exit(void); #ifdef __cplusplus } -#endif +#endif /* __cplusplus */ #endif /* __MENDER_LOG_H__ */ diff --git a/include/mender-ota.h b/include/mender-ota.h old mode 100644 new mode 100755 index 25cb0ae..9f4aa87 --- a/include/mender-ota.h +++ b/include/mender-ota.h @@ -30,7 +30,7 @@ #ifdef __cplusplus extern "C" { -#endif +#endif /* __cplusplus */ #include "mender-common.h" @@ -86,6 +86,6 @@ mender_err_t mender_ota_mark_app_invalid_rollback_and_reboot(void); #ifdef __cplusplus } -#endif +#endif /* __cplusplus */ #endif /* __MENDER_OTA_H__ */ diff --git a/include/mender-rtos.h b/include/mender-rtos.h old mode 100644 new mode 100755 index 1119b3f..31c0edd --- a/include/mender-rtos.h +++ b/include/mender-rtos.h @@ -30,66 +30,105 @@ #ifdef __cplusplus extern "C" { -#endif +#endif /* __cplusplus */ #include "mender-common.h" /** - * @brief Function used to create a new task - * @param task_function Task function - * @param name Name of the task - * @param stack_size Stack size - * @param arg Arguments - * @param priority priority of the task - * @param handle Task handle if the function succeeds, NULL otherwise + * @brief Work parameters */ -void mender_rtos_task_create(void (*task_function)(void *), char *name, size_t stack_size, void *arg, int priority, void **handle); +typedef struct { + mender_err_t (*function)(void); /**< Work function */ + uint32_t period; /**< Work period (seconds) */ + char * name; /**< Work name */ +} mender_rtos_work_params_t; /** - * @brief Fucntion used to delete a task - * @param handle Task handle + * @brief Initialization of the RTOS + * @return MENDER_OK if the function succeeds, error code otherwise */ -void mender_rtos_task_delete(void *handle); +mender_err_t mender_rtos_init(void); + +/** + * @brief Function used to register a new work + * @param work_params Work parameters + * @param handle Work handle if the function succeeds, NULL otherwise + * @return MENDER_OK if the function succeeds, error code otherwise + */ +mender_err_t mender_rtos_work_create(mender_rtos_work_params_t *work_params, void **handle); + +/** + * @brief Function used to activate a work + * @param handle Work handle + * @return MENDER_OK if the function succeeds, error code otherwise + */ +mender_err_t mender_rtos_work_activate(void *handle); + +/** + * @brief Function used to deactivate a work + * @param handle Work handle + * @return MENDER_OK if the function succeeds, error code otherwise + */ +mender_err_t mender_rtos_work_deactivate(void *handle); + +/** + * @brief Function used to delete a work + * @param handle Work handle + * @return MENDER_OK if the function succeeds, error code otherwise + */ +mender_err_t mender_rtos_work_delete(void *handle); /** * @brief Function used to initialize handle to be used with mender_rtos_delay_until_* functions * @param handle Delay handle + * @return MENDER_OK if the function succeeds, error code otherwise */ -void mender_rtos_delay_until_init(unsigned long *handle); +mender_err_t mender_rtos_delay_until_init(unsigned long *handle); /** * @brief Function used to make a delay until a specified time * @param delay Delay value (seconds) + * @return MENDER_OK if the function succeeds, error code otherwise */ -void mender_rtos_delay_until_s(unsigned long *handle, uint32_t delay); +mender_err_t mender_rtos_delay_until_s(unsigned long *handle, uint32_t delay); /** * @brief Function used to create a mutex - * @return Sempahore handle if the function succeeds, NULL otherwise + * @param handle Mutex handle if the function succeeds, NULL otherwise + * @return MENDER_OK if the function succeeds, error code otherwise + */ +mender_err_t mender_rtos_mutex_create(void **handle); + +/** + * @brief Function used to take a mutex + * @param handle Mutex handle + * @param delay_ms Delay to obtain the mutex, -1 to block indefinitely (without a timeout) + * @return MENDER_OK if the function succeeds, error code otherwise */ -void *mender_rtos_semaphore_create_mutex(void); +mender_err_t mender_rtos_mutex_take(void *handle, int32_t delay_ms); /** - * @brief Function used to take a semaphore - * @param handle Semaphore handle - * @param delay_ms Delay to obtain the semaphore, -1 to block indefinitely (without a timeout) + * @brief Function used to give a mutex + * @param handle Mutex handle + * @return MENDER_OK if the function succeeds, error code otherwise */ -void mender_rtos_semaphore_take(void *handle, int32_t delay_ms); +mender_err_t mender_rtos_mutex_give(void *handle); /** - * @brief Function used to give a semaphore - * @param handle Semaphore handle + * @brief Function used to delete a mutex + * @param handle Mutex handle + * @return MENDER_OK if the function succeeds, error code otherwise */ -void mender_rtos_semaphore_give(void *handle); +mender_err_t mender_rtos_mutex_delete(void *handle); /** - * @brief Function used to delete a semaphore - * @param handle Semaphore handle + * @brief Release mender RTOS + * @return MENDER_OK if the function succeeds, error code otherwise */ -void mender_rtos_semaphore_delete(void *handle); +mender_err_t mender_rtos_exit(void); #ifdef __cplusplus } -#endif +#endif /* __cplusplus */ #endif /* __MENDER_RTOS_H__ */ diff --git a/include/mender-storage.h b/include/mender-storage.h old mode 100644 new mode 100755 index 82c17a4..c442b1a --- a/include/mender-storage.h +++ b/include/mender-storage.h @@ -30,7 +30,7 @@ #ifdef __cplusplus extern "C" { -#endif +#endif /* __cplusplus */ #include "mender-common.h" @@ -101,6 +101,6 @@ mender_err_t mender_storage_exit(void); #ifdef __cplusplus } -#endif +#endif /* __cplusplus */ #endif /* __MENDER_STORAGE_H__ */ diff --git a/include/mender-tls.h b/include/mender-tls.h old mode 100644 new mode 100755 index c7bcd76..e5537a5 --- a/include/mender-tls.h +++ b/include/mender-tls.h @@ -30,7 +30,7 @@ #ifdef __cplusplus extern "C" { -#endif +#endif /* __cplusplus */ #include "mender-common.h" @@ -84,6 +84,6 @@ mender_err_t mender_tls_exit(void); #ifdef __cplusplus } -#endif +#endif /* __cplusplus */ #endif /* __MENDER_TLS_H__ */ diff --git a/include/mender-untar.h b/include/mender-untar.h old mode 100644 new mode 100755 index fc13c3c..afd424e --- a/include/mender-untar.h +++ b/include/mender-untar.h @@ -30,7 +30,7 @@ #ifdef __cplusplus extern "C" { -#endif +#endif /* __cplusplus */ #include "mender-common.h" @@ -90,6 +90,6 @@ void mender_untar_release(mender_untar_ctx_t *ctx); #ifdef __cplusplus } -#endif +#endif /* __cplusplus */ #endif /* __MENDER_UNTAR_H__ */ diff --git a/include/mender-utils.h b/include/mender-utils.h old mode 100644 new mode 100755 index f28fd8d..c67401d --- a/include/mender-utils.h +++ b/include/mender-utils.h @@ -30,7 +30,7 @@ #ifdef __cplusplus extern "C" { -#endif +#endif /* __cplusplus */ #include "mender-common.h" @@ -50,6 +50,6 @@ char *mender_utils_deployment_status_to_string(mender_deployment_status_t deploy #ifdef __cplusplus } -#endif +#endif /* __cplusplus */ #endif /* __MENDER_UTILS_H__ */ diff --git a/platform/http/zephyr/src/mender-http.c b/platform/http/zephyr/src/mender-http.c old mode 100644 new mode 100755 index 26b5b58..da41b97 --- a/platform/http/zephyr/src/mender-http.c +++ b/platform/http/zephyr/src/mender-http.c @@ -27,9 +27,9 @@ #include #include -#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) +#ifdef CONFIG_NET_SOCKETS_SOCKOPT_TLS #include -#endif +#endif /* CONFIG_NET_SOCKETS_SOCKOPT_TLS */ #include #include "mender-http.h" #include "mender-log.h" @@ -284,7 +284,10 @@ mender_http_connect(const char *host, const char *port, int *sock) { hints.ai_protocol = IPPROTO_TCP; /* Perform DNS resolution of the host */ - mender_rtos_delay_until_init(&last_wake_time); + if (MENDER_OK != (ret = mender_rtos_delay_until_init(&last_wake_time))) { + mender_log_error("Unable to initialize delay"); + goto END; + } while (resolve_attempts--) { if (0 == (result = getaddrinfo(host, port, &hints, &addr))) { break; @@ -300,18 +303,18 @@ mender_http_connect(const char *host, const char *port, int *sock) { } /* Create socket */ -#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) +#ifdef CONFIG_NET_SOCKETS_SOCKOPT_TLS if ((result = socket(addr->ai_family, SOCK_STREAM, IPPROTO_TLS_1_2)) < 0) { #else if ((result = socket(addr->ai_family, SOCK_STREAM, IPPROTO_TCP)) < 0) { -#endif +#endif /* CONFIG_NET_SOCKETS_SOCKOPT_TLS */ mender_log_error("Unable to create socket, result = %d", result); ret = MENDER_FAIL; goto END; } *sock = result; -#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) +#ifdef CONFIG_NET_SOCKETS_SOCKOPT_TLS /* Set TLS_SEC_TAG_LIST option */ sec_tag_t sec_tag[] = { @@ -334,10 +337,13 @@ mender_http_connect(const char *host, const char *port, int *sock) { goto END; } -#endif +#endif /* CONFIG_NET_SOCKETS_SOCKOPT_TLS */ /* Connect to the host */ - mender_rtos_delay_until_init(&last_wake_time); + if (MENDER_OK != (ret = mender_rtos_delay_until_init(&last_wake_time))) { + mender_log_error("Unable to initialize delay"); + goto END; + } while (connect_attempts--) { if (0 == (result = connect(*sock, addr->ai_addr, addr->ai_addrlen))) { break; diff --git a/platform/rtos/freertos/src/mender-rtos.c b/platform/rtos/freertos/src/mender-rtos.c index abd21b9..896aee3 100644 --- a/platform/rtos/freertos/src/mender-rtos.c +++ b/platform/rtos/freertos/src/mender-rtos.c @@ -29,54 +29,354 @@ #include "FreeRTOS.h" #include "semphr.h" #include "task.h" +#include "timers.h" #else #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/task.h" -#endif +#include "freertos/timers.h" +#endif /* __has_include("FreeRTOS.h") */ +#include "mender-log.h" #include "mender-rtos.h" -void -mender_rtos_task_create(void (*task_function)(void *), char *name, size_t stack_size, void *arg, int priority, void **handle) { - xTaskCreate(task_function, name, (configSTACK_DEPTH_TYPE)(stack_size / sizeof(configSTACK_DEPTH_TYPE)), arg, (UBaseType_t)priority, (TaskHandle_t *)handle); +/** + * @brief Mender RTOS work queue stack + */ +#ifndef MENDER_RTOS_WORK_QUEUE_STACK_SIZE +#define MENDER_RTOS_WORK_QUEUE_STACK_SIZE (12 * 1024) +#endif /* MENDER_RTOS_WORK_QUEUE_STACK_SIZE */ + +/** + * @brief Mender RTOS work queue priority + */ +#ifndef MENDER_RTOS_WORK_QUEUE_PRIORITY +#define MENDER_RTOS_WORK_QUEUE_PRIORITY (5) +#endif /* MENDER_RTOS_WORK_QUEUE_PRIORITY */ + +/** + * @brief Mender RTOS work queue lenght + */ +#ifndef MENDER_RTOS_WORK_QUEUE_LENGTH +#define MENDER_RTOS_WORK_QUEUE_LENGTH (10) +#endif /* MENDER_RTOS_WORK_QUEUE_LENGTH */ + +/** + * @brief Work context + */ +typedef struct { + mender_rtos_work_params_t params; /**< Work parameters */ + SemaphoreHandle_t sem_handle; /**< Semaphore used to indicate work is pending or executing */ + TimerHandle_t timer_handle; /**< Timer used to periodically execute work */ +} mender_rtos_work_context_t; + +/** + * @brief Function used to handle work context timer when it expires + * @param handle Timer handler + */ +static void mender_rtos_timer_callback(TimerHandle_t handle); + +/** + * @brief Thread used to handle work queue + * @param arg Not used + */ +static void mender_rtos_work_queue_thread(void *arg); + +/** + * @brief Work queue handle + */ +static QueueHandle_t mender_rtos_work_queue_handle = NULL; + +mender_err_t +mender_rtos_init(void) { + + /* Create and start work queue */ + if (NULL == (mender_rtos_work_queue_handle = xQueueCreate(MENDER_RTOS_WORK_QUEUE_LENGTH, sizeof(mender_rtos_work_context_t *)))) { + mender_log_error("Unable to create work queue"); + return MENDER_FAIL; + } + if (pdPASS + != xTaskCreate(mender_rtos_work_queue_thread, + "mender", + (configSTACK_DEPTH_TYPE)(MENDER_RTOS_WORK_QUEUE_STACK_SIZE / sizeof(configSTACK_DEPTH_TYPE)), + NULL, + MENDER_RTOS_WORK_QUEUE_PRIORITY, + NULL)) { + mender_log_error("Unable to create work queue thread"); + return MENDER_FAIL; + } + + return MENDER_OK; +} + +mender_err_t +mender_rtos_work_create(mender_rtos_work_params_t *work_params, void **handle) { + + assert(NULL != work_params); + assert(NULL != work_params->function); + assert(NULL != work_params->name); + assert(NULL != handle); + + /* Create work context */ + mender_rtos_work_context_t *work_context = malloc(sizeof(mender_rtos_work_context_t)); + if (NULL == work_context) { + mender_log_error("Unable to allocate memory"); + goto FAIL; + } + memset(work_context, 0, sizeof(mender_rtos_work_context_t)); + + /* Copy work parameters */ + work_context->params.function = work_params->function; + work_context->params.period = work_params->period; + if (NULL == (work_context->params.name = strdup(work_params->name))) { + mender_log_error("Unable to allocate memory"); + goto FAIL; + } + + /* Create semaphore used to protect work function */ + if (NULL == (work_context->sem_handle = xSemaphoreCreateBinary())) { + mender_log_error("Unable to create semaphore"); + goto FAIL; + } + + /* Create timer to handle the work periodically */ + if (NULL + == (work_context->timer_handle = xTimerCreate( + work_context->params.name, (1000 * work_context->params.period) / portTICK_PERIOD_MS, pdTRUE, work_context, mender_rtos_timer_callback))) { + mender_log_error("Unable to create timer"); + goto FAIL; + } + + /* Return handle to the new work */ + *handle = (void *)work_context; + + return MENDER_OK; + +FAIL: + + /* Release memory */ + if (NULL != work_context) { + if (NULL != work_context->timer_handle) { + xTimerDelete(work_context->timer_handle, portMAX_DELAY); + } + if (NULL != work_context->sem_handle) { + vSemaphoreDelete(work_context->sem_handle); + } + if (NULL != work_context->params.name) { + free(work_context->params.name); + } + free(work_context); + } + + return MENDER_FAIL; } -void -mender_rtos_task_delete(void *handle) { - vTaskDelete(handle); +mender_err_t +mender_rtos_work_activate(void *handle) { + + assert(NULL != handle); + + /* Get work context */ + mender_rtos_work_context_t *work_context = (mender_rtos_work_context_t *)handle; + + /* Give timer used to protect the work function */ + if (pdTRUE != xSemaphoreGive(work_context->sem_handle)) { + mender_log_error("Unable to give semaphore"); + return MENDER_FAIL; + } + + /* Start the timer to handle the work */ + if (pdPASS != xTimerStart(work_context->timer_handle, portMAX_DELAY)) { + mender_log_error("Unable to start timer"); + return MENDER_FAIL; + } + + /* Execute the work now */ + mender_rtos_timer_callback(work_context->timer_handle); + + return MENDER_OK; } -void +mender_err_t +mender_rtos_work_deactivate(void *handle) { + + assert(NULL != handle); + + /* Get work context */ + mender_rtos_work_context_t *work_context = (mender_rtos_work_context_t *)handle; + + /* Stop the timer used to periodically execute the work (if it is running) */ + xTimerStop(work_context->timer_handle, portMAX_DELAY); + while (pdFALSE != xTimerIsTimerActive(work_context->timer_handle)) { + vTaskDelay(1); + } + + /* Wait if the work is pending or executing */ + if (pdPASS != xSemaphoreTake(work_context->sem_handle, portMAX_DELAY)) { + mender_log_error("Work '%s' is pending or executing", work_context->params.name); + return MENDER_FAIL; + } + + return MENDER_OK; +} + +mender_err_t +mender_rtos_work_delete(void *handle) { + + assert(NULL != handle); + + /* Get work context */ + mender_rtos_work_context_t *work_context = (mender_rtos_work_context_t *)handle; + + /* Release memory */ + xTimerDelete(work_context->timer_handle, portMAX_DELAY); + vSemaphoreDelete(work_context->sem_handle); + if (NULL != work_context->params.name) { + free(work_context->params.name); + } + free(work_context); + + return MENDER_OK; +} + +mender_err_t mender_rtos_delay_until_init(unsigned long *handle) { + assert(NULL != handle); + + /* Get uptime */ *handle = (unsigned long)xTaskGetTickCount(); + + return MENDER_OK; } -void +mender_err_t mender_rtos_delay_until_s(unsigned long *handle, uint32_t delay) { + + /* Sleep */ vTaskDelayUntil((TickType_t *)handle, (1000 * delay) / portTICK_PERIOD_MS); + + return MENDER_OK; } -void * -mender_rtos_semaphore_create_mutex(void) { - return (void *)xSemaphoreCreateMutex(); +mender_err_t +mender_rtos_mutex_create(void **handle) { + + assert(NULL != handle); + + /* Create mutex */ + if (NULL == (*handle = (void *)xSemaphoreCreateMutex())) { + return MENDER_FAIL; + } + + return MENDER_OK; } -void -mender_rtos_semaphore_take(void *handle, int32_t delay_ms) { - if (delay_ms >= 0) { - xSemaphoreTake((SemaphoreHandle_t)handle, delay_ms / portTICK_PERIOD_MS); - } else { - xSemaphoreTake((SemaphoreHandle_t)handle, portMAX_DELAY); +mender_err_t +mender_rtos_mutex_take(void *handle, int32_t delay_ms) { + + assert(NULL != handle); + + /* Take mutex */ + if (pdPASS != xSemaphoreTake((SemaphoreHandle_t)handle, (delay_ms >= 0) ? (delay_ms / portTICK_PERIOD_MS) : portMAX_DELAY)) { + return MENDER_FAIL; } + + return MENDER_OK; } -void -mender_rtos_semaphore_give(void *handle) { - xSemaphoreGive((SemaphoreHandle_t)handle); +mender_err_t +mender_rtos_mutex_give(void *handle) { + + assert(NULL != handle); + + /* Give mutex */ + if (pdPASS != xSemaphoreGive((SemaphoreHandle_t)handle)) { + return MENDER_FAIL; + } + + return MENDER_OK; } -void -mender_rtos_semaphore_delete(void *handle) { +mender_err_t +mender_rtos_mutex_delete(void *handle) { + + assert(NULL != handle); + + /* Release memory */ vSemaphoreDelete((SemaphoreHandle_t)handle); + + return MENDER_OK; +} + +mender_err_t +mender_rtos_exit(void) { + + /* Submit empty work to the work queue, this ask the work queue thread to terminate */ + mender_rtos_work_context_t *work_context = NULL; + if (pdPASS != xQueueSend(mender_rtos_work_queue_handle, &work_context, portMAX_DELAY)) { + mender_log_error("Unable to submit empty work to the work queue"); + return MENDER_FAIL; + } + + return MENDER_OK; +} + +static void +mender_rtos_timer_callback(TimerHandle_t handle) { + + assert(NULL != handle); + + /* Get work context */ + mender_rtos_work_context_t *work_context = (mender_rtos_work_context_t *)pvTimerGetTimerID(handle); + assert(NULL != work_context); + + /* Exit if the work is already pending or executing */ + if (pdPASS != xSemaphoreTake(work_context->sem_handle, 0)) { + mender_log_warning("Work '%s' is already pending or executing", work_context->params.name); + return; + } + + /* Submit the work to the work queue */ + if (pdPASS != xQueueSend(mender_rtos_work_queue_handle, &work_context, 0)) { + mender_log_warning("Unable to submit work '%s' to the work queue", work_context->params.name); + xSemaphoreGive(work_context->sem_handle); + } +} + +static void +mender_rtos_work_queue_thread(void *arg) { + + (void)arg; + mender_rtos_work_context_t *work_context = NULL; + + /* Handle work to be executed */ + while (pdPASS == xQueueReceive(mender_rtos_work_queue_handle, &work_context, portMAX_DELAY)) { + + /* Check if empty work is received from the work queue, this ask the work queue thread to terminate */ + if (NULL == work_context) { + goto END; + } + + /* Call work function */ + if (MENDER_DONE == work_context->params.function()) { + + /* Work is done, stop timer used to execute the work periodically */ + xTimerStop(work_context->timer_handle, portMAX_DELAY); + while (pdFALSE != xTimerIsTimerActive(work_context->timer_handle)) { + vTaskDelay(1); + } + } + + /* Release semaphore used to protect the work function */ + xSemaphoreGive(work_context->sem_handle); + } + +END: + + /* Release memory */ + vQueueDelete(mender_rtos_work_queue_handle); + mender_rtos_work_queue_handle = NULL; + + /* Terminate work queue thread */ + vTaskDelete(NULL); } diff --git a/platform/rtos/zephyr/src/mender-rtos.c b/platform/rtos/zephyr/src/mender-rtos.c old mode 100644 new mode 100755 index 624df69..d8f900a --- a/platform/rtos/zephyr/src/mender-rtos.c +++ b/platform/rtos/zephyr/src/mender-rtos.c @@ -26,94 +26,296 @@ */ #include +#include "mender-log.h" #include "mender-rtos.h" /** - * @brief Mender client task stack - * @note TODO Should be modified when a dynamic API to create stack will be available - * @ref https://github.com/zephyrproject-rtos/zephyr/issues/26999 - * @ref https://github.com/zephyrproject-rtos/zephyr/pull/44379 + * @brief Mender RTOS work queue stack */ -#ifndef MENDER_CLIENT_TASK_STACK_SIZE -#define MENDER_CLIENT_TASK_STACK_SIZE (14 * 1024) -#endif -K_THREAD_STACK_DEFINE(mender_client_task_stack, MENDER_CLIENT_TASK_STACK_SIZE); +#ifndef MENDER_RTOS_WORK_QUEUE_STACK_SIZE +#define MENDER_RTOS_WORK_QUEUE_STACK_SIZE (12 * 1024) +#endif /* MENDER_RTOS_WORK_QUEUE_STACK_SIZE */ +K_THREAD_STACK_DEFINE(mender_rtos_work_queue_stack, MENDER_RTOS_WORK_QUEUE_STACK_SIZE); /** - * @brief Generic thread entry wrapper - * @param p1 First param, used to pass task_function - * @param p2 Second param, used to pass arg - * @param p3 Third param, not used + * @brief Mender RTOS work queue priority */ -static void mender_rtos_thread_entry(void *p1, void *p2, void *p3); - -void -mender_rtos_task_create(void (*task_function)(void *), char *name, size_t stack_size, void *arg, int priority, void **handle) { - struct k_thread *thread = malloc(sizeof(struct k_thread)); - if (NULL != thread) { - k_thread_create( - thread, mender_client_task_stack, MENDER_CLIENT_TASK_STACK_SIZE, mender_rtos_thread_entry, task_function, arg, NULL, priority, K_USER, K_NO_WAIT); - k_thread_name_set(thread, name); +#ifndef MENDER_RTOS_WORK_QUEUE_PRIORITY +#define MENDER_RTOS_WORK_QUEUE_PRIORITY (5) +#endif /* MENDER_RTOS_WORK_QUEUE_PRIORITY */ + +/** + * @brief Work context + */ +typedef struct { + mender_rtos_work_params_t params; /**< Work parameters */ + struct k_sem sem_handle; /**< Semaphore used to indicate work is pending or executing */ + struct k_timer timer_handle; /**< Timer used to periodically execute work */ + struct k_work work_handle; /**< Work handle used to execute the work function */ +} mender_rtos_work_context_t; + +/** + * @brief Function used to handle work context timer when it expires + * @param handle Timer handler + */ +static void mender_rtos_timer_callback(struct k_timer *handle); + +/** + * @brief Function used to handle work + * @param handle Work handler + */ +static void mender_rtos_work_handler(struct k_work *handle); + +/** + * @brief Mender RTOS work queue handle + */ +static struct k_work_q mender_rtos_work_queue_handle; + +mender_err_t +mender_rtos_init(void) { + + /* Create and start work queue */ + k_work_queue_init(&mender_rtos_work_queue_handle); + k_work_queue_start(&mender_rtos_work_queue_handle, mender_rtos_work_queue_stack, MENDER_RTOS_WORK_QUEUE_STACK_SIZE, MENDER_RTOS_WORK_QUEUE_PRIORITY, NULL); + + return MENDER_OK; +} + +mender_err_t +mender_rtos_work_create(mender_rtos_work_params_t *work_params, void **handle) { + + assert(NULL != work_params); + assert(NULL != work_params->function); + assert(NULL != work_params->name); + assert(NULL != handle); + + /* Create work context */ + mender_rtos_work_context_t *work_context = malloc(sizeof(mender_rtos_work_context_t)); + if (NULL == work_context) { + mender_log_error("Unable to allocate memory"); + goto FAIL; + } + memset(work_context, 0, sizeof(mender_rtos_work_context_t)); + + /* Copy work parameters */ + work_context->params.function = work_params->function; + work_context->params.period = work_params->period; + if (NULL == (work_context->params.name = strdup(work_params->name))) { + mender_log_error("Unable to allocate memory"); + goto FAIL; } - *handle = (void *)thread; + + /* Create semaphore used to protect work function */ + if (0 != k_sem_init(&work_context->sem_handle, 0, 1)) { + mender_log_error("Unable to create semaphore"); + goto FAIL; + } + + /* Create timer to handle the work periodically */ + k_timer_init(&work_context->timer_handle, mender_rtos_timer_callback, NULL); + k_timer_user_data_set(&work_context->timer_handle, (void *)work_context); + + /* Create work used to execute work function */ + k_work_init(&work_context->work_handle, mender_rtos_work_handler); + + /* Return handle to the new work context */ + *handle = (void *)work_context; + + return MENDER_OK; + +FAIL: + + /* Release memory */ + if (NULL != work_context) { + if (NULL != work_context->params.name) { + free(work_context->params.name); + } + free(work_context); + } + + return MENDER_FAIL; +} + +mender_err_t +mender_rtos_work_activate(void *handle) { + + assert(NULL != handle); + + /* Get work context */ + mender_rtos_work_context_t *work_context = (mender_rtos_work_context_t *)handle; + + /* Give timer used to protect the work function */ + k_sem_give(&work_context->sem_handle); + + /* Start the timer to handle the work */ + k_timer_start(&work_context->timer_handle, K_NO_WAIT, K_MSEC(1000 * work_context->params.period)); + + return MENDER_OK; } -void -mender_rtos_task_delete(void *handle) { - if (NULL != handle) { - k_thread_abort(handle); +mender_err_t +mender_rtos_work_deactivate(void *handle) { + + assert(NULL != handle); + + /* Get work context */ + mender_rtos_work_context_t *work_context = (mender_rtos_work_context_t *)handle; + + /* Stop the timer used to periodically execute the work (if it is running) */ + k_timer_stop(&work_context->timer_handle); + + /* Wait if the work is pending or executing */ + if (0 != k_sem_take(&work_context->sem_handle, K_FOREVER)) { + mender_log_error("Work '%s' is pending or executing", work_context->params.name); + return MENDER_FAIL; } + + return MENDER_OK; } -void +mender_err_t +mender_rtos_work_delete(void *handle) { + + assert(NULL != handle); + + /* Get work context */ + mender_rtos_work_context_t *work_context = (mender_rtos_work_context_t *)handle; + + /* Release memory */ + if (NULL != work_context->params.name) { + free(work_context->params.name); + } + free(work_context); + + return MENDER_OK; +} + +mender_err_t mender_rtos_delay_until_init(unsigned long *handle) { + assert(NULL != handle); + + /* Get uptime */ *handle = (unsigned long)k_uptime_get(); + + return MENDER_OK; } -void +mender_err_t mender_rtos_delay_until_s(unsigned long *handle, uint32_t delay) { + assert(NULL != handle); + + /* Compute sleep time and sleep */ int64_t ms = (1000 * delay) - (k_uptime_get() - *handle); k_msleep((ms > 0) ? ((int32_t)ms) : 1); + + /* Update uptime */ *handle = (unsigned long)k_uptime_get(); + + return MENDER_OK; } -void * -mender_rtos_semaphore_create_mutex(void) { - struct k_mutex *mutex = malloc(sizeof(struct k_mutex)); - if (NULL != mutex) { - k_mutex_init(mutex); +mender_err_t +mender_rtos_mutex_create(void **handle) { + + assert(NULL != handle); + + /* Create mutex */ + if (NULL == (*handle = malloc(sizeof(struct k_mutex)))) { + return MENDER_FAIL; } - return mutex; + if (0 != k_mutex_init((struct k_mutex *)(*handle))) { + free(*handle); + *handle = NULL; + return MENDER_FAIL; + } + + return MENDER_OK; } -void -mender_rtos_semaphore_take(void *handle, int32_t delay_ms) { - if (delay_ms >= 0) { - k_mutex_lock((struct k_mutex *)handle, K_MSEC(delay_ms)); - } else { - k_mutex_lock((struct k_mutex *)handle, K_FOREVER); +mender_err_t +mender_rtos_mutex_take(void *handle, int32_t delay_ms) { + + assert(NULL != handle); + + /* Take mutex */ + if (0 != k_mutex_lock((struct k_mutex *)handle, (delay_ms >= 0) ? K_MSEC(delay_ms) : K_FOREVER)) { + return MENDER_FAIL; } + + return MENDER_OK; } -void -mender_rtos_semaphore_give(void *handle) { - k_mutex_unlock((struct k_mutex *)handle); +mender_err_t +mender_rtos_mutex_give(void *handle) { + + assert(NULL != handle); + + /* Give mutex */ + if (0 != k_mutex_unlock((struct k_mutex *)handle)) { + return MENDER_FAIL; + } + + return MENDER_OK; } -void -mender_rtos_semaphore_delete(void *handle) { +mender_err_t +mender_rtos_mutex_delete(void *handle) { + + assert(NULL != handle); + + /* Release memory */ free(handle); + + return MENDER_OK; +} + +mender_err_t +mender_rtos_exit(void) { + + /* Nothing to do */ + return MENDER_OK; +} + +static void +mender_rtos_timer_callback(struct k_timer *handle) { + + assert(NULL != handle); + + /* Get work context */ + mender_rtos_work_context_t *work_context = (mender_rtos_work_context_t *)k_timer_user_data_get(handle); + assert(NULL != work_context); + + /* Exit if the work is already pending or executing */ + if (0 != k_sem_take(&work_context->sem_handle, K_NO_WAIT)) { + mender_log_warning("Work '%s' is already pending or executing", work_context->params.name); + return; + } + + /* Submit the work to the work queue */ + if (k_work_submit_to_queue(&mender_rtos_work_queue_handle, &work_context->work_handle) < 0) { + mender_log_warning("Unable to submit work '%s' to the work queue", work_context->params.name); + k_sem_give(&work_context->sem_handle); + } } static void -mender_rtos_thread_entry(void *p1, void *p2, void *p3) { +mender_rtos_work_handler(struct k_work *handle) { + + assert(NULL != handle); - assert(NULL != p1); - void (*task_function)(void *) = p1; - (void)p3; + /* Get work context */ + mender_rtos_work_context_t *work_context = CONTAINER_OF(handle, mender_rtos_work_context_t, work_handle); + assert(NULL != work_context); + + /* Call work function */ + if (MENDER_DONE == work_context->params.function()) { + + /* Work is done, stop timer used to execute the work periodically */ + k_timer_stop(&work_context->timer_handle); + } - /* Call task function */ - task_function(p2); + /* Release semaphore used to protect the work function */ + k_sem_give(&work_context->sem_handle); } diff --git a/platform/tls/mbedtls/src/mender-tls.c b/platform/tls/mbedtls/src/mender-tls.c old mode 100644 new mode 100755 index dc9c64e..8f9da8c --- a/platform/tls/mbedtls/src/mender-tls.c +++ b/platform/tls/mbedtls/src/mender-tls.c @@ -31,7 +31,7 @@ #include "mbedtls/entropy.h" #ifdef MBEDTLS_ERROR_C #include "mbedtls/error.h" -#endif +#endif /* MBEDTLS_ERROR_C */ #include "mbedtls/pk.h" #include "mbedtls/rsa.h" #include "mbedtls/x509.h" @@ -43,17 +43,17 @@ */ #ifndef MENDER_TLS_PRIVATE_KEY_LENGTH #define MENDER_TLS_PRIVATE_KEY_LENGTH (2048) -#endif +#endif /* MENDER_TLS_PRIVATE_KEY_LENGTH */ #ifndef MENDER_TLS_PUBLIC_KEY_LENGTH #define MENDER_TLS_PUBLIC_KEY_LENGTH (768) -#endif +#endif /* MENDER_TLS_PUBLIC_KEY_LENGTH */ /** * @brief Default signature length (base64 encoded) */ #ifndef MENDER_TLS_SIGNATURE_LENGTH #define MENDER_TLS_SIGNATURE_LENGTH (512) -#endif +#endif /* MENDER_TLS_SIGNATURE_LENGTH */ mender_err_t mender_tls_init(void) { @@ -77,7 +77,7 @@ mender_tls_generate_authentication_keys(unsigned char **private_key, size_t *pri #ifdef MBEDTLS_ERROR_C char err[128]; -#endif +#endif /* MBEDTLS_ERROR_C */ /* Initialize mbedtls */ if (NULL == (pk_context = malloc(sizeof(mbedtls_pk_context)))) { @@ -106,7 +106,7 @@ mender_tls_generate_authentication_keys(unsigned char **private_key, size_t *pri mender_log_error("Unable to initialize ctr drbg (-0x%04x: %s)", -ret, err); #else mender_log_error("Unable to initialize ctr drbg (-0x%04x)", -ret); -#endif +#endif /* MBEDTLS_ERROR_C */ goto END; } @@ -117,7 +117,7 @@ mender_tls_generate_authentication_keys(unsigned char **private_key, size_t *pri mender_log_error("Unable to setup pk (-0x%04x: %s)", -ret, err); #else mender_log_error("Unable to setup pk (-0x%04x)", -ret); -#endif +#endif /* MBEDTLS_ERROR_C */ goto END; } @@ -128,7 +128,7 @@ mender_tls_generate_authentication_keys(unsigned char **private_key, size_t *pri mender_log_error("Unable to setup pk (-0x%04x: %s)", -ret, err); #else mender_log_error("Unable to setup pk (-0x%04x)", -ret); -#endif +#endif /* MBEDTLS_ERROR_C */ goto END; } @@ -144,7 +144,7 @@ mender_tls_generate_authentication_keys(unsigned char **private_key, size_t *pri mender_log_error("Unable to write private key to PEM format (-0x%04x: %s)", -ret, err); #else mender_log_error("Unable to write private key to PEM format (-0x%04x)", -ret); -#endif +#endif /* MBEDTLS_ERROR_C */ free(*private_key); *private_key = NULL; goto END; @@ -174,7 +174,7 @@ mender_tls_generate_authentication_keys(unsigned char **private_key, size_t *pri mender_log_error("Unable to write public key to PEM format (-0x%04x: %s)", -ret, err); #else mender_log_error("Unable to write public key to PEM format (-0x%04x)", -ret); -#endif +#endif /* MBEDTLS_ERROR_C */ free(*private_key); *private_key = NULL; free(*public_key); @@ -309,7 +309,7 @@ mender_tls_sign_payload(unsigned char *private_key, size_t private_key_length, c char * tmp; #ifdef MBEDTLS_ERROR_C char err[128]; -#endif +#endif /* MBEDTLS_ERROR_C */ /* Initialize mbedtls */ if (NULL == (pk_context = malloc(sizeof(mbedtls_pk_context)))) { @@ -338,7 +338,7 @@ mender_tls_sign_payload(unsigned char *private_key, size_t private_key_length, c mender_log_error("Unable to initialize ctr drbg (-0x%04x: %s)", -ret, err); #else mender_log_error("Unable to initialize ctr drbg (-0x%04x)", -ret); -#endif +#endif /* MBEDTLS_ERROR_C */ goto END; } @@ -347,13 +347,13 @@ mender_tls_sign_payload(unsigned char *private_key, size_t private_key_length, c if (0 != (ret = mbedtls_pk_parse_key(pk_context, private_key, private_key_length, NULL, 0, mbedtls_ctr_drbg_random, ctr_drbg))) { #else if (0 != (ret = mbedtls_pk_parse_key(pk_context, private_key, private_key_length, NULL, 0))) { -#endif +#endif /* MBEDTLS_VERSION_NUMBER >= 0x03000000 */ #ifdef MBEDTLS_ERROR_C mbedtls_strerror(ret, err, sizeof(err)); mender_log_error("Unable to parse private key (-0x%04x: %s)", -ret, err); #else mender_log_error("Unable to parse private key (-0x%04x)", -ret); -#endif +#endif /* MBEDTLS_ERROR_C */ goto END; } @@ -365,7 +365,7 @@ mender_tls_sign_payload(unsigned char *private_key, size_t private_key_length, c mender_log_error("Unable to generate digest (-0x%04x: %s)", -ret, err); #else mender_log_error("Unable to generate digest (-0x%04x)", -ret); -#endif +#endif /* MBEDTLS_ERROR_C */ goto END; } @@ -380,13 +380,13 @@ mender_tls_sign_payload(unsigned char *private_key, size_t private_key_length, c if (0 != (ret = mbedtls_pk_sign(pk_context, MBEDTLS_MD_SHA256, digest, sizeof(digest), sig, sig_length, &sig_length, mbedtls_ctr_drbg_random, ctr_drbg))) { #else if (0 != (ret = mbedtls_pk_sign(pk_context, MBEDTLS_MD_SHA256, digest, sizeof(digest), sig, &sig_length, mbedtls_ctr_drbg_random, ctr_drbg))) { -#endif +#endif /* MBEDTLS_VERSION_NUMBER >= 0x03000000 */ #ifdef MBEDTLS_ERROR_C mbedtls_strerror(ret, err, sizeof(err)); mender_log_error("Unable to compute signature (-0x%04x: %s)", -ret, err); #else mender_log_error("Unable to compute signature (-0x%04x)", -ret); -#endif +#endif /* MBEDTLS_ERROR_C */ goto END; } @@ -403,7 +403,7 @@ mender_tls_sign_payload(unsigned char *private_key, size_t private_key_length, c mender_log_error("Unable to encode signature (-0x%04x: %s)", -ret, err); #else mender_log_error("Unable to encode signature (-0x%04x)", -ret); -#endif +#endif /* MBEDTLS_ERROR_C */ free(*signature); *signature = NULL; goto END; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt old mode 100644 new mode 100755 index cb0a4e0..19cfebe --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -27,7 +27,7 @@ cmake_minimum_required(VERSION 3.16.3) # CMake configurations set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Configs" FORCE) -if (NOT CMAKE_BUILD_TYPE) +if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() @@ -55,29 +55,30 @@ message("Executable name: ${APP_EXECUTABLE_NAME}") add_executable(${APP_EXECUTABLE_NAME}) # Define compile options -if (CMAKE_BUILD_TYPE MATCHES "Debug") +if(CMAKE_BUILD_TYPE MATCHES "Debug") target_compile_definitions(${APP_EXECUTABLE_NAME} PRIVATE DEBUG) endif() +add_compile_definitions(CONFIG_MENDER_CLIENT_ADD_ON_INVENTORY) # Add definitions depending of the target -if (CONFIG_MENDER_MCU_CLIENT_BOARD_TYPE MATCHES "zephyr") +if(CONFIG_MENDER_MCU_CLIENT_BOARD_TYPE MATCHES "zephyr") add_compile_definitions(CONFIG_MENDER_STORAGE_SECTOR_COUNT=4) endif() -if (CONFIG_MENDER_MCU_CLIENT_HTTP_TYPE MATCHES "zephyr") +if(CONFIG_MENDER_MCU_CLIENT_HTTP_TYPE MATCHES "zephyr") add_compile_definitions(CONFIG_NET_SOCKETS_SOCKOPT_TLS) add_compile_definitions(CONFIG_MENDER_HTTP_CA_CERTIFICATE_TAG=1) endif() # Add sources -file(GLOB_RECURSE SOURCES_TEMP ${CMAKE_CURRENT_LIST_DIR}/src/*.c) +file(GLOB_RECURSE SOURCES_TEMP "${CMAKE_CURRENT_LIST_DIR}/src/*.c") target_sources(${APP_EXECUTABLE_NAME} PRIVATE ${SOURCES_TEMP}) # Include mocks -include(${CMAKE_CURRENT_LIST_DIR}/mocks/CMakeLists.txt) +include("${CMAKE_CURRENT_LIST_DIR}/mocks/CMakeLists.txt") # Include mender-mcu-client library -include_directories(${CMAKE_CURRENT_LIST_DIR}/../include) -include(${CMAKE_CURRENT_LIST_DIR}/../CMakeLists.txt) +include_directories("${CMAKE_CURRENT_LIST_DIR}/../include") +include("${CMAKE_CURRENT_LIST_DIR}/../CMakeLists.txt") # Link the executable with the mender-mcu-library target_link_libraries(${APP_EXECUTABLE_NAME} mender-mcu-client pthread) @@ -86,6 +87,6 @@ target_link_libraries(${APP_EXECUTABLE_NAME} mender-mcu-client pthread) project(${PROJECT_BASE_NAME} LANGUAGES C ASM) # Define VERSION if not already done -file(STRINGS ${CMAKE_CURRENT_LIST_DIR}/../VERSION VERSION_NUMBER LIMIT_COUNT 1) +file(STRINGS "${CMAKE_CURRENT_LIST_DIR}/../VERSION" VERSION_NUMBER LIMIT_COUNT 1) STRING(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)-rc[0-9]+" "\\1.\\2.\\3" VERSION_NUMBER "${VERSION_NUMBER}") project(${PROJECT_BASE_NAME} VERSION ${VERSION_NUMBER} LANGUAGES) diff --git a/tests/mocks/CMakeLists.txt b/tests/mocks/CMakeLists.txt old mode 100644 new mode 100755 index 22c0d1a..770c661 --- a/tests/mocks/CMakeLists.txt +++ b/tests/mocks/CMakeLists.txt @@ -24,8 +24,8 @@ # SOFTWARE. # Include all mocks -include(${CMAKE_CURRENT_LIST_DIR}/cjson/CMakeLists.txt) -include(${CMAKE_CURRENT_LIST_DIR}/esp-idf/CMakeLists.txt) -include(${CMAKE_CURRENT_LIST_DIR}/freertos/CMakeLists.txt) -include(${CMAKE_CURRENT_LIST_DIR}/mbedtls/CMakeLists.txt) -include(${CMAKE_CURRENT_LIST_DIR}/zephyr/CMakeLists.txt) +include("${CMAKE_CURRENT_LIST_DIR}/cjson/CMakeLists.txt") +include("${CMAKE_CURRENT_LIST_DIR}/esp-idf/CMakeLists.txt") +include("${CMAKE_CURRENT_LIST_DIR}/freertos/CMakeLists.txt") +include("${CMAKE_CURRENT_LIST_DIR}/mbedtls/CMakeLists.txt") +include("${CMAKE_CURRENT_LIST_DIR}/zephyr/CMakeLists.txt") diff --git a/tests/mocks/cjson/CMakeLists.txt b/tests/mocks/cjson/CMakeLists.txt old mode 100644 new mode 100755 index 376083f..c4122eb --- a/tests/mocks/cjson/CMakeLists.txt +++ b/tests/mocks/cjson/CMakeLists.txt @@ -24,18 +24,19 @@ # SOFTWARE. # Fetch repository -set(GIT_REPO_URL "https://github.com/DaveGamble/cJSON") -set(GIT_TAG_NAME "v1.7.15") +set(GIT_REPO_URL "https://github.com/DaveGamble/cJSON") +set(GIT_TAG_NAME "v1.7.15") # Fetch destination -set(GIT_FOLDER_NAME ${CMAKE_CURRENT_LIST_DIR}/cjson) +set(GIT_FOLDER_NAME "${CMAKE_CURRENT_LIST_DIR}/cjson") # Declare fetch content include(FetchContent) FetchContent_Declare( cjson - URL ${GIT_REPO_URL}/archive/refs/tags/${GIT_TAG_NAME}.zip - SOURCE_DIR ${GIT_FOLDER_NAME} + URL "${GIT_REPO_URL}/archive/refs/tags/${GIT_TAG_NAME}.zip" + SOURCE_DIR "${GIT_FOLDER_NAME}" + DOWNLOAD_EXTRACT_TIMESTAMP TRUE ) # Fetch if not already done @@ -46,7 +47,7 @@ if(NOT cjson_POPULATED) endif() # Add sources -target_sources(${APP_EXECUTABLE_NAME} PRIVATE ${GIT_FOLDER_NAME}/cJSON.c) +target_sources(${APP_EXECUTABLE_NAME} PRIVATE "${GIT_FOLDER_NAME}/cJSON.c") # Add include directories include_directories(${GIT_FOLDER_NAME}) diff --git a/tests/mocks/esp-idf/CMakeLists.txt b/tests/mocks/esp-idf/CMakeLists.txt old mode 100644 new mode 100755 index af90b83..4225171 --- a/tests/mocks/esp-idf/CMakeLists.txt +++ b/tests/mocks/esp-idf/CMakeLists.txt @@ -24,8 +24,8 @@ # SOFTWARE. # Add sources -file(GLOB_RECURSE SOURCES_TEMP ${CMAKE_CURRENT_LIST_DIR}/src/*.c) +file(GLOB_RECURSE SOURCES_TEMP "${CMAKE_CURRENT_LIST_DIR}/src/*.c") target_sources(${APP_EXECUTABLE_NAME} PRIVATE ${SOURCES_TEMP}) # Add include directories -include_directories(${CMAKE_CURRENT_LIST_DIR}/include) +include_directories("${CMAKE_CURRENT_LIST_DIR}/include") diff --git a/tests/mocks/freertos/CMakeLists.txt b/tests/mocks/freertos/CMakeLists.txt old mode 100644 new mode 100755 index b0bd9ea..bbed392 --- a/tests/mocks/freertos/CMakeLists.txt +++ b/tests/mocks/freertos/CMakeLists.txt @@ -24,18 +24,19 @@ # SOFTWARE. # Fetch repository -set(GIT_REPO_URL "https://github.com/FreeRTOS/FreeRTOS-Kernel") -set(GIT_TAG_NAME "V10.5.1") +set(GIT_REPO_URL "https://github.com/FreeRTOS/FreeRTOS-Kernel") +set(GIT_TAG_NAME "V10.5.1") # Fetch destination -set(GIT_FOLDER_NAME ${CMAKE_CURRENT_LIST_DIR}/freertos) +set(GIT_FOLDER_NAME "${CMAKE_CURRENT_LIST_DIR}/freertos") # Declare fetch content include(FetchContent) FetchContent_Declare( freertos - URL ${GIT_REPO_URL}/archive/refs/tags/${GIT_TAG_NAME}.zip - SOURCE_DIR ${GIT_FOLDER_NAME} + URL "${GIT_REPO_URL}/archive/refs/tags/${GIT_TAG_NAME}.zip" + SOURCE_DIR "${GIT_FOLDER_NAME}" + DOWNLOAD_EXTRACT_TIMESTAMP TRUE ) # Fetch if not already done @@ -46,10 +47,21 @@ if(NOT freertos_POPULATED) endif() # Add sources -target_sources(${APP_EXECUTABLE_NAME} PRIVATE ${GIT_FOLDER_NAME}/croutine.c ${GIT_FOLDER_NAME}/event_groups.c ${GIT_FOLDER_NAME}/list.c ${GIT_FOLDER_NAME}/queue.c ${GIT_FOLDER_NAME}/stream_buffer.c ${GIT_FOLDER_NAME}/tasks.c ${GIT_FOLDER_NAME}/timers.c ${GIT_FOLDER_NAME}/portable/MemMang/heap_3.c ${GIT_FOLDER_NAME}/portable/ThirdParty/GCC/Posix/port.c ${GIT_FOLDER_NAME}/portable/ThirdParty/GCC/Posix/utils/wait_for_event.c) +target_sources(${APP_EXECUTABLE_NAME} PRIVATE + "${GIT_FOLDER_NAME}/croutine.c" + "${GIT_FOLDER_NAME}/event_groups.c" + "${GIT_FOLDER_NAME}/list.c" + "${GIT_FOLDER_NAME}/queue.c" + "${GIT_FOLDER_NAME}/stream_buffer.c" + "${GIT_FOLDER_NAME}/tasks.c" + "${GIT_FOLDER_NAME}/timers.c" + "${GIT_FOLDER_NAME}/portable/MemMang/heap_3.c" + "${GIT_FOLDER_NAME}/portable/ThirdParty/GCC/Posix/port.c" + "${GIT_FOLDER_NAME}/portable/ThirdParty/GCC/Posix/utils/wait_for_event.c" +) # Add include directories -include_directories(${GIT_FOLDER_NAME}/include) -include_directories(${GIT_FOLDER_NAME}/portable/ThirdParty/GCC/Posix) -include_directories(${GIT_FOLDER_NAME}/portable/ThirdParty/GCC/Posix/utils) -include_directories(${CMAKE_CURRENT_LIST_DIR}/include) +include_directories("${GIT_FOLDER_NAME}/include") +include_directories("${GIT_FOLDER_NAME}/portable/ThirdParty/GCC/Posix") +include_directories("${GIT_FOLDER_NAME}/portable/ThirdParty/GCC/Posix/utils") +include_directories("${CMAKE_CURRENT_LIST_DIR}/include") diff --git a/tests/mocks/mbedtls/CMakeLists.txt b/tests/mocks/mbedtls/CMakeLists.txt old mode 100644 new mode 100755 index 50cbb2c..d473f35 --- a/tests/mocks/mbedtls/CMakeLists.txt +++ b/tests/mocks/mbedtls/CMakeLists.txt @@ -24,18 +24,19 @@ # SOFTWARE. # Fetch repository -set(GIT_REPO_URL "https://github.com/Mbed-TLS/mbedtls") -set(GIT_TAG_NAME "v2.25.0") +set(GIT_REPO_URL "https://github.com/Mbed-TLS/mbedtls") +set(GIT_TAG_NAME "v2.25.0") # Fetch destination -set(GIT_FOLDER_NAME ${CMAKE_CURRENT_LIST_DIR}/mbedtls) +set(GIT_FOLDER_NAME "${CMAKE_CURRENT_LIST_DIR}/mbedtls") # Declare fetch content include(FetchContent) FetchContent_Declare( mbedtls - URL ${GIT_REPO_URL}/archive/refs/tags/${GIT_TAG_NAME}.zip - SOURCE_DIR ${GIT_FOLDER_NAME} + URL "${GIT_REPO_URL}/archive/refs/tags/${GIT_TAG_NAME}.zip" + SOURCE_DIR "${GIT_FOLDER_NAME}" + DOWNLOAD_EXTRACT_TIMESTAMP TRUE ) # Fetch if not already done @@ -46,8 +47,8 @@ if(NOT mbedtls_POPULATED) endif() # Add sources -file(GLOB_RECURSE SOURCES_TEMP ${GIT_FOLDER_NAME}/library/*.c) +file(GLOB_RECURSE SOURCES_TEMP "${GIT_FOLDER_NAME}/library/*.c") target_sources(${APP_EXECUTABLE_NAME} PRIVATE ${SOURCES_TEMP}) # Add include directories -include_directories(${GIT_FOLDER_NAME}/include) +include_directories("${GIT_FOLDER_NAME}/include") diff --git a/tests/mocks/zephyr/CMakeLists.txt b/tests/mocks/zephyr/CMakeLists.txt old mode 100644 new mode 100755 index c03c7ee..8320396 --- a/tests/mocks/zephyr/CMakeLists.txt +++ b/tests/mocks/zephyr/CMakeLists.txt @@ -24,8 +24,8 @@ # SOFTWARE. # Add sources -file(GLOB_RECURSE SOURCES_TEMP ${CMAKE_CURRENT_LIST_DIR}/src/*.c) +file(GLOB_RECURSE SOURCES_TEMP "${CMAKE_CURRENT_LIST_DIR}/src/*.c") target_sources(${APP_EXECUTABLE_NAME} PRIVATE ${SOURCES_TEMP}) # Add include directories -include_directories(${CMAKE_CURRENT_LIST_DIR}/include) +include_directories("${CMAKE_CURRENT_LIST_DIR}/include") diff --git a/tests/mocks/zephyr/include/zephyr/kernel.h b/tests/mocks/zephyr/include/zephyr/kernel.h index f25d4b8..cd42d1a 100644 --- a/tests/mocks/zephyr/include/zephyr/kernel.h +++ b/tests/mocks/zephyr/include/zephyr/kernel.h @@ -3,6 +3,7 @@ #include #include +#include #include struct k_thread { @@ -11,6 +12,21 @@ struct k_thread { struct k_mutex { void *dummy; }; +struct k_sem { + void *dummy; +}; +struct k_timer { + void *dummy; +}; +struct k_work { + void *dummy; +}; +struct k_work_queue_config { + void *dummy; +}; +struct k_work_q { + void *dummy; +}; typedef struct k_thread *k_tid_t; @@ -18,6 +34,11 @@ typedef char *k_thread_stack_t; typedef void (*k_thread_entry_t)(void *p1, void *p2, void *p3); +typedef void (*k_timer_expiry_t)(struct k_timer *timer); +typedef void (*k_timer_stop_t)(struct k_timer *timer); + +typedef void (*k_work_handler_t)(struct k_work *work); + #define K_USER 4 #define K_NO_WAIT ((k_timeout_t) { .ticks = (0) }) @@ -44,6 +65,22 @@ int k_mutex_init(struct k_mutex *mutex); int k_mutex_lock(struct k_mutex *mutex, k_timeout_t timeout); int k_mutex_unlock(struct k_mutex *mutex); +int k_sem_init(struct k_sem *sem, unsigned int initial_count, unsigned int limit); +int k_sem_take(struct k_sem *sem, k_timeout_t timeout); +void k_sem_give(struct k_sem *sem); + +void k_timer_init(struct k_timer *timer, k_timer_expiry_t expiry_fn, k_timer_stop_t stop_fn); +void k_timer_user_data_set(struct k_timer *timer, void *user_data); +void *k_timer_user_data_get(const struct k_timer *timer); +void k_timer_start(struct k_timer *timer, k_timeout_t duration, k_timeout_t period); +void k_timer_stop(struct k_timer *timer); + +void k_work_init(struct k_work *work, k_work_handler_t handler); +int k_work_submit_to_queue(struct k_work_q *queue, struct k_work *work); + +void k_work_queue_init(struct k_work_q *queue); +void k_work_queue_start(struct k_work_q *queue, k_thread_stack_t *stack, size_t stack_size, int prio, const struct k_work_queue_config *cfg); + int64_t k_uptime_get(void); int32_t k_msleep(int32_t ms); diff --git a/tests/mocks/zephyr/include/zephyr/sys/util.h b/tests/mocks/zephyr/include/zephyr/sys/util.h new file mode 100644 index 0000000..a4a6985 --- /dev/null +++ b/tests/mocks/zephyr/include/zephyr/sys/util.h @@ -0,0 +1,6 @@ +#ifndef __UTIL_H__ +#define __UTIL_H__ + +#define CONTAINER_OF(ptr, type, field) ((type *)(((char *)(ptr)) - offsetof(type, field))) + +#endif /* __UTIL_H__ */ diff --git a/tests/mocks/zephyr/src/kernel.c b/tests/mocks/zephyr/src/kernel.c index f874221..69e0872 100644 --- a/tests/mocks/zephyr/src/kernel.c +++ b/tests/mocks/zephyr/src/kernel.c @@ -38,6 +38,58 @@ k_mutex_unlock(struct k_mutex *mutex) { return 0; } +int +k_sem_init(struct k_sem *sem, unsigned int initial_count, unsigned int limit) { + return 0; +} + +int +k_sem_take(struct k_sem *sem, k_timeout_t timeout) { + return 0; +} + +void +k_sem_give(struct k_sem *sem) { +} + +void +k_timer_init(struct k_timer *timer, k_timer_expiry_t expiry_fn, k_timer_stop_t stop_fn) { +} + +void +k_timer_user_data_set(struct k_timer *timer, void *user_data) { +} + +void * +k_timer_user_data_get(const struct k_timer *timer) { + return NULL; +} + +void +k_timer_start(struct k_timer *timer, k_timeout_t duration, k_timeout_t period) { +} + +void +k_timer_stop(struct k_timer *timer) { +} + +void +k_work_init(struct k_work *work, k_work_handler_t handler) { +} + +int +k_work_submit_to_queue(struct k_work_q *queue, struct k_work *work) { + return 0; +} + +void +k_work_queue_init(struct k_work_q *queue) { +} + +void +k_work_queue_start(struct k_work_q *queue, k_thread_stack_t *stack, size_t stack_size, int prio, const struct k_work_queue_config *cfg) { +} + int64_t k_uptime_get(void) { return 0; diff --git a/tests/src/main.c b/tests/src/main.c index 25cf82f..e6b2ddd 100644 --- a/tests/src/main.c +++ b/tests/src/main.c @@ -28,6 +28,7 @@ #include #include "mender-client.h" +#include "mender-inventory.h" #include "mender-ota.h" /** @@ -46,9 +47,7 @@ main(int argc, char **argv) { .host = "https://hosted.mender.io", .tenant_token = NULL, .authentication_poll_interval = 0, - .inventory_poll_interval = 0, .update_poll_interval = 0, - .restart_poll_interval = 0, .recommissioning = false }; mender_client_callbacks_t mender_client_callbacks = { .authentication_success = NULL, .authentication_failure = NULL, @@ -62,6 +61,17 @@ main(int argc, char **argv) { mender_client_init(&mender_client_config, &mender_client_callbacks); + /* Initialize mender add-ons */ +#ifdef CONFIG_MENDER_CLIENT_ADD_ON_INVENTORY + mender_inventory_config_t mender_inventory_config = { .artifact_name = "artifact_name", .device_type = "device_type", .poll_interval = 0 }; + mender_inventory_init(&mender_inventory_config); +#endif /* CONFIG_MENDER_CLIENT_ADD_ON_INVENTORY */ + + /* Release mender add-ons */ +#ifdef CONFIG_MENDER_CLIENT_ADD_ON_INVENTORY + mender_inventory_exit(); +#endif /* CONFIG_MENDER_CLIENT_ADD_ON_INVENTORY */ + /* Release mender-client */ mender_client_exit(); diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt old mode 100644 new mode 100755 index 416dc23..e021c9c --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -1,5 +1,5 @@ # @file CMakeLists.txt -# @brief mender-mcu-client module CMakeLists file +# @brief mender-mcu-client zephyr module CMakeLists file # # MIT License # @@ -24,18 +24,21 @@ # SOFTWARE. if(CONFIG_MENDER_MCU_CLIENT) - zephyr_include_directories(../include) + zephyr_include_directories("${CMAKE_CURRENT_LIST_DIR}/../include") zephyr_library() zephyr_library_sources( - ../core/src/mender-api.c - ../core/src/mender-client.c - ../core/src/mender-untar.c - ../core/src/mender-utils.c - ../platform/board/zephyr/src/mender-log.c - ../platform/board/zephyr/src/mender-ota.c - ../platform/board/zephyr/src/mender-storage.c - ../platform/http/zephyr/src/mender-http.c - ../platform/rtos/zephyr/src/mender-rtos.c - ../platform/tls/mbedtls/src/mender-tls.c + "${CMAKE_CURRENT_LIST_DIR}/../core/src/mender-api.c" + "${CMAKE_CURRENT_LIST_DIR}/../core/src/mender-client.c" + "${CMAKE_CURRENT_LIST_DIR}/../core/src/mender-untar.c" + "${CMAKE_CURRENT_LIST_DIR}/../core/src/mender-utils.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/board/zephyr/src/mender-log.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/board/zephyr/src/mender-ota.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/board/zephyr/src/mender-storage.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/http/zephyr/src/mender-http.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/rtos/zephyr/src/mender-rtos.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/tls/mbedtls/src/mender-tls.c" + ) + zephyr_library_sources_ifdef(CONFIG_MENDER_CLIENT_ADD_ON_INVENTORY + "${CMAKE_CURRENT_LIST_DIR}/../add-ons/src/mender-inventory.c" ) endif() diff --git a/zephyr/Kconfig b/zephyr/Kconfig index 828de40..748f558 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -24,40 +24,85 @@ # SOFTWARE. menuconfig MENDER_MCU_CLIENT - bool"Mender Firmware Over-the-Air support" - select BOOTLOADER_MCUBOOT - select CJSON - select DNS_RESOLVER - select FLASH - select HTTP_CLIENT - select IMG_ENABLE_IMAGE_CHECK - select IMG_ERASE_PROGRESSIVELY - select IMG_MANAGER - select MBEDTLS - select MPU_ALLOW_FLASH_WRITE - select NETWORKING - select NET_TCP - select NET_SOCKETS - select NET_SOCKETS_POSIX_NAMES - select NVS - select REBOOT - help - Secure, risk tolerant and efficient over-the-air updates for all device software + bool "Mender Firmware Over-the-Air support" + select BOOTLOADER_MCUBOOT + select CJSON + select DNS_RESOLVER + select FLASH + select FLASH_MAP + select HTTP_CLIENT + select IMG_ENABLE_IMAGE_CHECK + select IMG_ERASE_PROGRESSIVELY + select IMG_MANAGER + select MBEDTLS + select MPU_ALLOW_FLASH_WRITE + select NETWORKING + select NET_TCP + select NET_SOCKETS + select NET_SOCKETS_POSIX_NAMES + select NVS + select REBOOT + select STREAM_FLASH + help + Secure, risk tolerant and efficient over-the-air updates for all device software if MENDER_MCU_CLIENT +config MENDER_SERVER_HOST + string "Mender server host URL" + default "https://hosted.mender.io" + help + Set the Mender server host URL to be used on the device. + +config MENDER_SERVER_TENANT_TOKEN + string "Mender server Tenant Token" + help + Set the Mender server Tenant Token, to be used with https://hosted.mender.io. + +config MENDER_CLIENT_AUTHENTICATION_POLL_INTERVAL + int "Mender client Authentication poll interval (seconds)" + range 0 3600 + default 600 + help + Interval used to periodically try to authenticate to the mender server until it succeeds. + +config MENDER_CLIENT_UPDATE_POLL_INTERVAL + int "Mender client Update poll interval (seconds)" + range 0 86400 + default 1800 + help + Interval used to periodically check for new deployments on the mender server. + config MENDER_STORAGE_SECTOR_COUNT - int "Number of sectors of the mender_storage partition" - default 2 - range 2 8 - help - Number of sectors of the mender_storage partition, must match the configuration of the partition + int "Number of sectors of the mender_storage partition" + default 2 + range 2 8 + help + Number of sectors of the mender_storage partition, must match the configuration of the partition config MENDER_HTTP_CA_CERTIFICATE_TAG - int "CA certificate tag" - default 1 - help - A security tag that ROOT CA server credential will be referenced with, see tls_credential_add + int "CA certificate tag" + default 1 + help + A security tag that ROOT CA server credential will be referenced with, see tls_credential_add + +config MENDER_CLIENT_ADD_ON_INVENTORY + bool "Mender client Inventory" + default y + help + Inventory add-ons permits to send inventory key-value pairs to the Mender server. + It is particurlarly used to send artifact name and device type, and it permits to see the last check-in time of the device. + +if MENDER_CLIENT_ADD_ON_INVENTORY + +config MENDER_CLIENT_INVENTORY_POLL_INTERVAL + int "Mender client Inventory poll interval (seconds)" + range 0 86400 + default 28800 + help + Interval used to periodically send inventory to the mender server. + +endif module = MENDER module-str = Log Level for mender