Skip to content

Commit

Permalink
Anjay 2.5.0
Browse files Browse the repository at this point in the history
Features:
- Updated AvsCommons to 4.2.1
- Added new API for etag allocation
- (commercial version only) Added initial support for Enrollment Over Secure Transport (EST)

Bugfixes:
- Fixed segfault in CoAP downloads caused by cancellation in the middle of the transfer
- Fixed building tests on CentOS
- Fixed compilation when WITH_ANJAY_LOGS=OFF is used
- Fixed handling of transactional LwM2M Write
- (commercial version only) Fixed download suspension for downloads over shared socket
  • Loading branch information
Mateusz Krawiec committed Jul 14, 2020
1 parent 041126d commit a763991
Show file tree
Hide file tree
Showing 46 changed files with 605 additions and 200 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Expand Up @@ -15,7 +15,7 @@
cmake_minimum_required(VERSION 3.4.0)

project(anjay C)
set(ANJAY_VERSION "2.4.4" CACHE STRING "Anjay library version")
set(ANJAY_VERSION "2.5.0" CACHE STRING "Anjay library version")
set(ANJAY_BINARY_VERSION 1.0.0)

set(ANJAY_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
Expand Down
6 changes: 6 additions & 0 deletions README.md
Expand Up @@ -28,6 +28,7 @@ The project has been created and is actively maintained by [AVSystem](https://ww
* [Alternative build systems](#alternative-build-systems)
* [Use a Dockerfile](#use-a-dockerfile)
* [Mbed OS port](#mbed-os-port)
* [Zephyr OS port](#zephyr-os-port)
* [License](#license)
* [Commercial support](#commercial-support)
* [Contributing](#contributing)
Expand Down Expand Up @@ -80,6 +81,7 @@ This version includes full support for OMA LwM2M TS 1.0 features. Version that s
- any embedded platform (e.g. FreeRTOS, ThreadX) with lwIP networking stack
- porting is possible for any other platform that has ISO C99 compiler available, see [Porting guide for non-POSIX platforms](https://avsystem.github.io/Anjay-doc/PortingGuideForNonPOSIXPlatforms.html) for details
- preimplemented [integration layer for Arm Mbed OS](https://github.com/AVSystem/Anjay-mbedos) and an [example client based on it](https://github.com/AVSystem/Anjay-mbedos-client) are available
- [example client](https://github.com/AVSystem/Anjay-zephyr-client) based on Zephyr OS is available

- CoAP data formats:
- TLV
Expand Down Expand Up @@ -266,6 +268,10 @@ docker run -it anjay

If you want to use Anjay on Mbed OS, you might be interested in the [Anjay-mbedos](https://github.com/AVSystem/Anjay-mbedos) and [Anjay-mbedos-client](https://github.com/AVSystem/Anjay-mbedos-client) repositories, which contain basic integration with that system.

## Zephyr OS port

If you want to use Anjay on Zephyr OS, you might want to check our [example client](https://github.com/AVSystem/Anjay-zephyr-client) based on it.

## License

See [LICENSE](LICENSE) file.
Expand Down
17 changes: 10 additions & 7 deletions demo/demo.c
Expand Up @@ -65,13 +65,16 @@ static int security_object_reload(anjay_demo_t *demo) {
* anjay_security_object_add_instance will make a deep copy by itself.
*/
instance.server_uri = server->uri;
instance.public_cert_or_psk_identity =
args->public_cert_or_psk_identity;
instance.public_cert_or_psk_identity_size =
args->public_cert_or_psk_identity_size;
instance.private_cert_or_psk_key = args->private_cert_or_psk_key;
instance.private_cert_or_psk_key_size =
args->private_cert_or_psk_key_size;
if (instance.security_mode != ANJAY_SECURITY_EST
|| server->is_bootstrap) {
instance.public_cert_or_psk_identity =
args->public_cert_or_psk_identity;
instance.public_cert_or_psk_identity_size =
args->public_cert_or_psk_identity_size;
instance.private_cert_or_psk_key = args->private_cert_or_psk_key;
instance.private_cert_or_psk_key_size =
args->private_cert_or_psk_key_size;
}
instance.server_public_key = args->server_public_key;
instance.server_public_key_size = args->server_public_key_size;

Expand Down
5 changes: 4 additions & 1 deletion demo/demo_args.c
Expand Up @@ -75,6 +75,7 @@ static int parse_security_mode(const char *mode_string,
{ "rpk", ANJAY_SECURITY_RPK },
{ "cert", ANJAY_SECURITY_CERTIFICATE },
{ "nosec", ANJAY_SECURITY_NOSEC },
{ "est", ANJAY_SECURITY_EST },
// clang-format on
};

Expand Down Expand Up @@ -1044,7 +1045,9 @@ int demo_parse_argv(cmdline_args_t *parsed_args, int argc, char *argv[]) {
retval = -1;
}
} else if (parsed_args->connection_args.security_mode
== ANJAY_SECURITY_CERTIFICATE) {
== ANJAY_SECURITY_CERTIFICATE
|| parsed_args->connection_args.security_mode
== ANJAY_SECURITY_EST) {
if (identity_set ^ key_set) {
demo_log(ERROR, "Setting public cert but not private cert (and "
"other way around) makes little sense");
Expand Down
59 changes: 31 additions & 28 deletions demo/firmware_update.c
Expand Up @@ -330,16 +330,18 @@ static int preprocess_firmware(fw_update_logic_t *fw) {
return result;
}

static int store_etag(avs_persistence_context_t *ctx,
const anjay_etag_t *etag) {
// UINT16_MAX is a magic value that means "there is no ETag"
uint16_t size16 = (etag ? etag->size : UINT16_MAX);
avs_error_t err = avs_persistence_u16(ctx, &size16);
if (avs_is_ok(err) && etag) {
err = avs_persistence_bytes(ctx, (uint8_t *) (intptr_t) etag->value,
etag->size);
}
return avs_is_ok(err) ? 0 : -1;
static avs_error_t store_etag(avs_persistence_context_t *ctx,
const anjay_etag_t *etag) {
bool use_etag = (etag != NULL);
avs_error_t err;

(void) (avs_is_err((err = avs_persistence_bool(ctx, &use_etag))) || !etag
|| avs_is_err((err = avs_persistence_u8(
ctx, (uint8_t *) (intptr_t) &etag->size)))
|| avs_is_err((err = avs_persistence_bytes(
ctx, (uint8_t *) (intptr_t) etag->value,
etag->size))));
return err;
}

static int write_persistence_file(const char *path,
Expand All @@ -360,7 +362,7 @@ static int write_persistence_file(const char *path,
|| avs_is_err(avs_persistence_string(&ctx, &download_file))
|| avs_is_err(avs_persistence_bool(&ctx,
&filename_administratively_set))
|| store_etag(&ctx, etag)) {
|| avs_is_err(store_etag(&ctx, etag))) {
demo_log(ERROR, "Could not write firmware state persistence file");
retval = -1;
}
Expand Down Expand Up @@ -563,24 +565,25 @@ static anjay_fw_update_handlers_t FW_UPDATE_HANDLERS = {
.get_coap_tx_params = fw_get_coap_tx_params
};

static int restore_etag(avs_persistence_context_t *ctx, anjay_etag_t **etag) {
static avs_error_t restore_etag(avs_persistence_context_t *ctx,
anjay_etag_t **etag) {
assert(etag && !*etag);
uint16_t size16;
avs_error_t err = avs_persistence_u16(ctx, &size16);
if (avs_is_ok(err) && size16 <= UINT8_MAX) {
*etag = (anjay_etag_t *) avs_malloc(offsetof(anjay_etag_t, value)
+ size16);
if (!*etag) {
return -1;
}
(*etag)->size = (uint8_t) size16;
if (avs_is_err((err = avs_persistence_bytes(ctx, (*etag)->value,
size16)))) {
avs_free(*etag);
*etag = NULL;
}
bool use_etag;
avs_error_t err;
uint8_t size8;
if (avs_is_err((err = avs_persistence_bool(ctx, &use_etag))) || !use_etag
|| avs_is_err((err = avs_persistence_u8(ctx, &size8)))
|| avs_is_err((err = ((*etag = anjay_etag_new(size8))
? avs_errno(AVS_NO_ERROR)
: avs_errno(AVS_ENOMEM))))) {
return err;
}

if (avs_is_err(err = avs_persistence_bytes(ctx, (*etag)->value, size8))) {
avs_free(*etag);
*etag = NULL;
}
return avs_is_ok(err) ? 0 : -1;
return err;
}

static bool is_valid_result(int8_t result) {
Expand Down Expand Up @@ -623,7 +626,7 @@ static persistence_file_data_t read_persistence_file(const char *path) {
|| avs_is_err(avs_persistence_string(&ctx, &data.download_file))
|| avs_is_err(avs_persistence_bool(
&ctx, &data.filename_administratively_set))
|| restore_etag(&ctx, &data.etag)) {
|| avs_is_err(restore_etag(&ctx, &data.etag))) {
demo_log(WARNING,
"Invalid data in the firmware state persistence file");
avs_free(data.uri);
Expand Down
73 changes: 69 additions & 4 deletions demo/objects/binary_app_data_container.c
Expand Up @@ -85,6 +85,7 @@ typedef struct binary_app_data_container_instance_struct {
typedef struct binary_app_data_container_struct {
const anjay_dm_object_def_t *def;
AVS_LIST(binary_app_data_container_instance_t) instances;
AVS_LIST(binary_app_data_container_instance_t) saved_instances;
} binary_app_data_container_t;

static inline binary_app_data_container_t *
Expand Down Expand Up @@ -367,6 +368,71 @@ static int list_resource_instances(anjay_t *anjay,
}
}

static int transaction_begin(anjay_t *anjay,
const anjay_dm_object_def_t *const *obj_ptr) {
(void) anjay;
binary_app_data_container_t *repr = get_obj(obj_ptr);
if (!repr->instances) {
return 0;
}

AVS_LIST(binary_app_data_container_instance_t) *saved_instances_tail =
&repr->saved_instances;
AVS_LIST(binary_app_data_container_instance_t) instance;
bool failed = false;
AVS_LIST_FOREACH(instance, repr->instances) {
AVS_LIST(binary_app_data_container_instance_t) saved_instance =
AVS_LIST_APPEND_NEW(binary_app_data_container_instance_t,
saved_instances_tail);
if (!saved_instance) {
demo_log(ERROR, "cannot allocate a new instance");
failed = true;
break;
}
if (instance->data_list) {
saved_instance->data_list =
AVS_LIST_SIMPLE_CLONE(instance->data_list);
if (!saved_instance->data_list) {
demo_log(ERROR, "cannot clone resource instances list");
failed = true;
break;
}
}
saved_instance->iid = instance->iid;
AVS_LIST_ADVANCE_PTR(&saved_instances_tail);
}

if (failed) {
AVS_LIST_CLEAR(&repr->saved_instances) {
AVS_LIST_CLEAR(&repr->saved_instances->data_list);
}
return ANJAY_ERR_INTERNAL;
}
return 0;
}

static int transaction_commit(anjay_t *anjay,
const anjay_dm_object_def_t *const *obj_ptr) {
(void) anjay;
binary_app_data_container_t *repr = get_obj(obj_ptr);
AVS_LIST_CLEAR(&repr->saved_instances) {
AVS_LIST_CLEAR(&repr->saved_instances->data_list);
}
return 0;
}

static int transaction_rollback(anjay_t *anjay,
const anjay_dm_object_def_t *const *obj_ptr) {
(void) anjay;
binary_app_data_container_t *repr = get_obj(obj_ptr);
AVS_LIST_CLEAR(&repr->instances) {
AVS_LIST_CLEAR(&repr->instances->data_list);
}
repr->instances = repr->saved_instances;
repr->saved_instances = NULL;
return 0;
}

static const anjay_dm_object_def_t OBJ_DEF = {
.oid = 19,
.handlers = {
Expand All @@ -381,11 +447,10 @@ static const anjay_dm_object_def_t OBJ_DEF = {
.resource_reset = resource_reset,
.list_resource_instances = list_resource_instances,

// TODO: implement these if transactional write/create is required
.transaction_begin = anjay_dm_transaction_NOOP,
.transaction_begin = transaction_begin,
.transaction_validate = anjay_dm_transaction_NOOP,
.transaction_commit = anjay_dm_transaction_NOOP,
.transaction_rollback = anjay_dm_transaction_NOOP
.transaction_commit = transaction_commit,
.transaction_rollback = transaction_rollback
}
};

Expand Down
29 changes: 27 additions & 2 deletions demo/objects/device.c
Expand Up @@ -83,6 +83,10 @@ typedef struct {
time_t current_time_offset;
char utc_offset[16];
char timezone[32];

time_t saved_current_time_offset;
char saved_utc_offset[16];
char saved_timezone[32];
} dev_repr_t;

static inline dev_repr_t *get_dev(const anjay_dm_object_def_t *const *obj_ptr) {
Expand Down Expand Up @@ -383,6 +387,27 @@ static int dev_resource_instances(anjay_t *anjay,
}
}

static int dev_transaction_begin(anjay_t *anjay,
const anjay_dm_object_def_t *const *obj_ptr) {
(void) anjay;
dev_repr_t *repr = get_dev(obj_ptr);
repr->saved_current_time_offset = repr->current_time_offset;
strcpy(repr->saved_utc_offset, repr->utc_offset);
strcpy(repr->saved_timezone, repr->timezone);
return 0;
}

static int
dev_transaction_rollback(anjay_t *anjay,
const anjay_dm_object_def_t *const *obj_ptr) {
(void) anjay;
dev_repr_t *repr = get_dev(obj_ptr);
repr->current_time_offset = repr->saved_current_time_offset;
strcpy(repr->utc_offset, repr->saved_utc_offset);
strcpy(repr->timezone, repr->saved_timezone);
return 0;
}

static const anjay_dm_object_def_t DEVICE = {
.oid = DEMO_OID_DEVICE,
.handlers = {
Expand All @@ -393,10 +418,10 @@ static const anjay_dm_object_def_t DEVICE = {
.resource_write = dev_write,
.resource_execute = dev_execute,
.list_resource_instances = dev_resource_instances,
.transaction_begin = anjay_dm_transaction_NOOP,
.transaction_begin = dev_transaction_begin,
.transaction_validate = anjay_dm_transaction_NOOP,
.transaction_commit = anjay_dm_transaction_NOOP,
.transaction_rollback = anjay_dm_transaction_NOOP
.transaction_rollback = dev_transaction_rollback
}
};

Expand Down
18 changes: 6 additions & 12 deletions doc/sphinx/source/FirmwareUpdateTutorial/FU6.rst
Expand Up @@ -118,7 +118,7 @@ the state from persistent storage:

.. highlight:: c
.. snippet-source:: examples/tutorial/firmware-update/download-resumption/src/firmware_update.c
:emphasize-lines: 1, 10-121, 130-132
:emphasize-lines: 1, 10-119, 128-130

#define _DEFAULT_SOURCE // for fileno()
#include "./firmware_update.h"
Expand Down Expand Up @@ -177,15 +177,10 @@ the state from persistent storage:
if (fread(&size, sizeof(size), 1, fp) != 1 || size == 0) {
return -1;
}
// NOTE: anjay_etag_t is a flexible array member, which means it
// has an array of runtime-allocated size at the end of it. That's
// why it is created using avs_malloc().
anjay_etag_t *etag =
(anjay_etag_t *) avs_malloc(offsetof(anjay_etag_t, value) + size);
anjay_etag_t *etag = anjay_etag_new(size);
if (!etag) {
return -1;
}
etag->size = size;
if (fread(etag->value, size, 1, fp) != 1) {
avs_free(etag);
Expand Down Expand Up @@ -254,6 +249,8 @@ the state from persistent storage:
download_state_t download_state;
} FW_STATE;
static const char *FW_IMAGE_DOWNLOAD_NAME = "/tmp/firmware_image.bin";
In the next section, we'll discuss when state storing and restoring should
be done.

Expand Down Expand Up @@ -359,14 +356,10 @@ accordingly:
}
anjay_etag_t *etag_copy = NULL;
if (!result && package_etag) {
const size_t etag_size =
offsetof(anjay_etag_t, value) + package_etag->size;
etag_copy = (anjay_etag_t *) avs_malloc(etag_size);
etag_copy = anjay_etag_clone(package_etag);
if (!etag_copy) {
fprintf(stderr, "Could not duplicate package ETag\n");
result = -1;
} else {
memcpy(etag_copy, package_etag, etag_size);
}
}
if (!result) {
Expand All @@ -380,6 +373,7 @@ accordingly:
return fw_open_download_file(0);
}


Then, we can implement storing the download state logic in ``fw_stream_write``:

.. highlight:: c
Expand Down
11 changes: 11 additions & 0 deletions example_configs/embedded_lwm2m10/anjay/anjay_config.h
Expand Up @@ -272,6 +272,17 @@
*/
/* #undef ANJAY_WITH_CBOR */

/**
* Enable support for Enrollment over Secure Transport.
*
* Requires <c>ANJAY_WITH_LWM2M11</c> and <c>ANJAY_WITH_BOOTSTRAP</c> to be
* enabled.
*
* IMPORTANT: Only available in the commercial version. Ignored in the open
* source version.
*/
/* #undef ANJAY_WITH_EST */

/**
* Enable support for custom "con" attribute that controls Confirmable
* notifications.
Expand Down

0 comments on commit a763991

Please sign in to comment.