diff --git a/subsys/mgmt/hawkbit/Kconfig b/subsys/mgmt/hawkbit/Kconfig index ec1a3078fe8d44a..237d503806e34a9 100644 --- a/subsys/mgmt/hawkbit/Kconfig +++ b/subsys/mgmt/hawkbit/Kconfig @@ -21,6 +21,7 @@ menuconfig HAWKBIT select MPU_ALLOW_FLASH_WRITE select IMG_ENABLE_IMAGE_CHECK select IMG_ERASE_PROGRESSIVELY + imply STREAM_FLASH_PROGRESS help hawkBit is a domain independent back-end framework for polling out software updates to constrained edge devices as well as more powerful @@ -120,6 +121,13 @@ config HAWKBIT_DEVICE_ID_MAX_LENGTH help Maximum length of the device id. +config HAWKBIT_SAVE_PROGRESS + bool "Save the hawkBit update download progress" + depends on STREAM_FLASH_PROGRESS + default y + help + Save the hawkBit update download progress. + module = HAWKBIT module-str = Log Level for hawkbit module-help = Enables logging for hawkBit code. diff --git a/subsys/mgmt/hawkbit/hawkbit.c b/subsys/mgmt/hawkbit/hawkbit.c index 5ddc005fba8cbcb..1f1fd1a30d6ccf2 100644 --- a/subsys/mgmt/hawkbit/hawkbit.c +++ b/subsys/mgmt/hawkbit/hawkbit.c @@ -45,6 +45,7 @@ LOG_MODULE_REGISTER(hawkbit, CONFIG_HAWKBIT_LOG_LEVEL); #define SHA256_HASH_SIZE 32 #define RESPONSE_BUFFER_SIZE 1100 #define DDI_SECURITY_TOKEN_SIZE 32 +#define RANGE_HEADER_SIZE 50 #define HAWKBIT_RECV_TIMEOUT (300 * MSEC_PER_SEC) #define HAWKBIT_SET_SERVER_TIMEOUT K_MSEC(300) @@ -105,8 +106,6 @@ static struct hawkbit_config { #endif /* CONFIG_HAWKBIT_DDI_NO_SECURITY */ struct hawkbit_download { - int download_status; - int download_progress; size_t downloaded_size; size_t http_content_size; uint8_t file_hash[SHA256_HASH_SIZE]; @@ -795,118 +794,143 @@ int hawkbit_init(void) return ret; } -static void response_cb(struct http_response *rsp, enum http_final_call final_data, void *userdata) +static void response_json_cb(struct http_response *rsp, enum http_final_call final_data, + struct hawkbit_context *hb_context) { - struct hawkbit_context *hb_context = userdata; size_t body_len; - int ret, downloaded; + int ret; uint8_t *body_data = NULL, *rsp_tmp = NULL; - if (rsp->http_status_code != 200) { - LOG_ERR("HTTP request denied: %d", rsp->http_status_code); - if (rsp->http_status_code == 401 || rsp->http_status_code == 403) { - hb_context->code_status = HAWKBIT_PERMISSION_ERROR; - } else { - hb_context->code_status = HAWKBIT_METADATA_ERROR; - } - return; + if (hb_context->dl.http_content_size == 0) { + hb_context->dl.http_content_size = rsp->content_length; } - switch (hb_context->type) { - case HAWKBIT_PROBE: - case HAWKBIT_PROBE_DEPLOYMENT_BASE: - if (hb_context->dl.http_content_size == 0) { - hb_context->dl.http_content_size = rsp->content_length; - } + if (rsp->body_found) { + body_data = rsp->body_frag_start; + body_len = rsp->body_frag_len; - if (rsp->body_found) { - body_data = rsp->body_frag_start; - body_len = rsp->body_frag_len; - - if ((hb_context->dl.downloaded_size + body_len) > - hb_context->response_buffer_size) { - hb_context->response_buffer_size = - hb_context->dl.downloaded_size + body_len; - rsp_tmp = realloc(hb_context->response_data, - hb_context->response_buffer_size); - if (rsp_tmp == NULL) { - LOG_ERR("Failed to realloc memory"); - hb_context->code_status = HAWKBIT_ALLOC_ERROR; - break; - } - - hb_context->response_data = rsp_tmp; + if ((hb_context->dl.downloaded_size + body_len) > + hb_context->response_buffer_size) { + hb_context->response_buffer_size = + hb_context->dl.downloaded_size + body_len; + rsp_tmp = realloc(hb_context->response_data, + hb_context->response_buffer_size); + if (rsp_tmp == NULL) { + LOG_ERR("Failed to realloc memory"); + hb_context->code_status = HAWKBIT_ALLOC_ERROR; + return; } - strncpy(hb_context->response_data + hb_context->dl.downloaded_size, - body_data, body_len); - hb_context->dl.downloaded_size += body_len; + + hb_context->response_data = rsp_tmp; + } + strncpy(hb_context->response_data + hb_context->dl.downloaded_size, body_data, + body_len); + hb_context->dl.downloaded_size += body_len; + } + + if (final_data == HTTP_DATA_FINAL) { + if (hb_context->dl.http_content_size != hb_context->dl.downloaded_size) { + LOG_ERR("HTTP response len mismatch, expected %d, got %d", + hb_context->dl.http_content_size, hb_context->dl.downloaded_size); + hb_context->code_status = HAWKBIT_METADATA_ERROR; + return; } - if (final_data == HTTP_DATA_FINAL) { - if (hb_context->dl.http_content_size != hb_context->dl.downloaded_size) { - LOG_ERR("HTTP response len mismatch, expected %d, got %d", - hb_context->dl.http_content_size, - hb_context->dl.downloaded_size); + hb_context->response_data[hb_context->dl.downloaded_size] = '\0'; + memset(&hb_context->results, 0, sizeof(hb_context->results)); + if (hb_context->type == HAWKBIT_PROBE) { + ret = json_obj_parse(hb_context->response_data, + hb_context->dl.downloaded_size, json_ctl_res_descr, + ARRAY_SIZE(json_ctl_res_descr), + &hb_context->results.base); + if (ret < 0) { + LOG_ERR("JSON parse error (%s): %d", "HAWKBIT_PROBE", ret); hb_context->code_status = HAWKBIT_METADATA_ERROR; - break; } - - hb_context->response_data[hb_context->dl.downloaded_size] = '\0'; - memset(&hb_context->results, 0, sizeof(hb_context->results)); - if (hb_context->type == HAWKBIT_PROBE) { - ret = json_obj_parse( - hb_context->response_data, hb_context->dl.downloaded_size, - json_ctl_res_descr, ARRAY_SIZE(json_ctl_res_descr), - &hb_context->results.base); - if (ret < 0) { - LOG_ERR("JSON parse error (%s): %d", "HAWKBIT_PROBE", ret); - hb_context->code_status = HAWKBIT_METADATA_ERROR; - } - } else { - ret = json_obj_parse( - hb_context->response_data, hb_context->dl.downloaded_size, - json_dep_res_descr, ARRAY_SIZE(json_dep_res_descr), - &hb_context->results.dep); - if (ret < 0) { - LOG_ERR("JSON parse error (%s): %d", "deploymentBase", ret); - hb_context->code_status = HAWKBIT_METADATA_ERROR; - } + } else { + ret = json_obj_parse(hb_context->response_data, + hb_context->dl.downloaded_size, json_dep_res_descr, + ARRAY_SIZE(json_dep_res_descr), + &hb_context->results.dep); + if (ret < 0) { + LOG_ERR("JSON parse error (%s): %d", "deploymentBase", ret); + hb_context->code_status = HAWKBIT_METADATA_ERROR; } } + } +} - break; - - case HAWKBIT_DOWNLOAD: - if (hb_context->dl.http_content_size == 0) { - hb_context->dl.http_content_size = rsp->content_length; +static void response_download_cb(struct http_response *rsp, enum http_final_call final_data, + struct hawkbit_context *hb_context) +{ + size_t body_len; + int ret, downloaded; + uint8_t *body_data = NULL; + static uint8_t download_progress; + + if (hb_context->dl.http_content_size == 0) { + hb_context->dl.http_content_size = rsp->content_length; + download_progress = 0; + if (IS_ENABLED(CONFIG_HAWKBIT_SAVE_PROGRESS) && rsp->http_status_code != 206) { + hb_context->flash_ctx.stream.bytes_written = 0; } + } - if (rsp->body_found) { - body_data = rsp->body_frag_start; - body_len = rsp->body_frag_len; + if (rsp->body_found) { + body_data = rsp->body_frag_start; + body_len = rsp->body_frag_len; - ret = flash_img_buffered_write(&hb_context->flash_ctx, body_data, body_len, - final_data == HTTP_DATA_FINAL); - if (ret < 0) { - LOG_ERR("Failed to write flash: %d", ret); - hb_context->code_status = HAWKBIT_DOWNLOAD_ERROR; - break; - } + ret = flash_img_buffered_write(&hb_context->flash_ctx, body_data, body_len, + final_data == HTTP_DATA_FINAL); + if (ret < 0) { + LOG_ERR("Failed to write flash: %d", ret); + hb_context->code_status = HAWKBIT_DOWNLOAD_ERROR; + return; } - hb_context->dl.downloaded_size = flash_img_bytes_written(&hb_context->flash_ctx); + IF_ENABLED(CONFIG_HAWKBIT_SAVE_PROGRESS, + (stream_flash_progress_save(&hb_context->flash_ctx.stream, + "hawkbit_download");)) + } - downloaded = - hb_context->dl.downloaded_size * 100 / hb_context->dl.http_content_size; + hb_context->dl.downloaded_size = flash_img_bytes_written(&hb_context->flash_ctx); - if (downloaded > hb_context->dl.download_progress) { - hb_context->dl.download_progress = downloaded; - LOG_DBG("Downloaded: %d%% ", hb_context->dl.download_progress); - } + downloaded = hb_context->dl.downloaded_size * 100 / hb_context->dl.file_size; - if (final_data == HTTP_DATA_FINAL) { - hb_context->final_data_received = true; + if (downloaded > download_progress) { + download_progress = downloaded; + LOG_DBG("Downloaded: %u%% (%u / %u)", download_progress, + hb_context->dl.downloaded_size, hb_context->dl.file_size); + } + + if (final_data == HTTP_DATA_FINAL) { + hb_context->final_data_received = true; + } +} + +static void response_cb(struct http_response *rsp, enum http_final_call final_data, void *userdata) +{ + struct hawkbit_context *hb_context = userdata; + + if (!IN_RANGE(rsp->http_status_code, 200, 299)) { + LOG_ERR("HTTP request denied: %d", rsp->http_status_code); + if (rsp->http_status_code == 401 || rsp->http_status_code == 403) { + hb_context->code_status = HAWKBIT_PERMISSION_ERROR; + } else { + hb_context->code_status = HAWKBIT_METADATA_ERROR; } + return; + } + + switch (hb_context->type) { + case HAWKBIT_PROBE: + case HAWKBIT_PROBE_DEPLOYMENT_BASE: + response_json_cb(rsp, final_data, hb_context); + + break; + + case HAWKBIT_DOWNLOAD: + response_download_cb(rsp, final_data, hb_context); break; @@ -986,6 +1010,13 @@ static bool send_request(struct hawkbit_context *hb_context, enum hawkbit_http_r * Resource for software module (Deployment Base) * GET: /{tenant}/controller/v1/{controllerId}/deploymentBase/{actionId} */ + + http_req.method = HTTP_GET; + hb_context->dl.http_content_size = 0; + hb_context->dl.downloaded_size = 0; + + break; + case HAWKBIT_DOWNLOAD: /* * Resource for software module (Deployment Base) @@ -994,7 +1025,23 @@ static bool send_request(struct hawkbit_context *hb_context, enum hawkbit_http_r */ http_req.method = HTTP_GET; hb_context->dl.http_content_size = 0; + +#ifdef CONFIG_HAWKBIT_SAVE_PROGRESS + hb_context->dl.downloaded_size = flash_img_bytes_written(&hb_context->flash_ctx); + if (IN_RANGE(hb_context->dl.downloaded_size, 1, hb_context->dl.file_size)) { + char header_range[RANGE_HEADER_SIZE] = {0}; + + snprintf(header_range, sizeof(header_range), "Range: bytes=%u-" HTTP_CRLF, + hb_context->dl.downloaded_size); + const char *const headers_range[] = {header_range, NULL}; + + http_req.optional_headers = (const char **)headers_range; + LOG_DBG("optional header: %s", header_range); + LOG_INF("Resuming download from %d bytes", hb_context->dl.downloaded_size); + } +#else hb_context->dl.downloaded_size = 0; +#endif break; @@ -1376,6 +1423,10 @@ static void s_download(void *o) flash_img_init(&s->hb_context.flash_ctx); + IF_ENABLED( + CONFIG_HAWKBIT_SAVE_PROGRESS, + (stream_flash_progress_load(&s->hb_context.flash_ctx.stream, "hawkbit_download");)) + if (!send_request(&s->hb_context, HAWKBIT_DOWNLOAD, url_buffer, NULL)) { LOG_ERR("Send request failed (%s)", "HAWKBIT_DOWNLOAD"); smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]); @@ -1390,6 +1441,10 @@ static void s_download(void *o) return; } + IF_ENABLED( + CONFIG_HAWKBIT_SAVE_PROGRESS, + (stream_flash_progress_clear(&s->hb_context.flash_ctx.stream, "hawkbit_download");)) + /* Verify the hash of the stored firmware */ struct flash_img_check fic = {0};