Skip to content

Commit

Permalink
architecture: rework to introduce add-ons and workqueue #8
Browse files Browse the repository at this point in the history
  • Loading branch information
joelguittet committed Apr 14, 2023
1 parent ae627eb commit 6cedf7c
Show file tree
Hide file tree
Showing 40 changed files with 1,742 additions and 619 deletions.
1 change: 1 addition & 0 deletions .clang-format-ignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/check_include_guards.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ on:
- '**'
pull_request:
types: [opened, synchronize, reopened]
schedule:
- cron: '0 0 1 * *'
jobs:
check:
name: Check code format
Expand Down Expand Up @@ -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
9 changes: 8 additions & 1 deletion CMakeLists.txt
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
31 changes: 21 additions & 10 deletions README.md
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down Expand Up @@ -79,42 +79,53 @@ 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).
* ...


## Architecture

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:
* `mender-ota`: implementation of functions used to write artifact in the memory.
* `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

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.
* ...


Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.0
0.3.0
232 changes: 232 additions & 0 deletions add-ons/src/mender-inventory.c
Original file line number Diff line number Diff line change
@@ -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 */
Loading

0 comments on commit 6cedf7c

Please sign in to comment.