1 /*
2 * Copyright (c) 2016-2017 Linaro Limited
3 * Copyright (c) 2018 Open Source Foundries Limited
4 * Copyright (c) 2018 Foundries.io
5 * Copyright (c) 2020 Linumiz
6 * Copyright (c) 2021 G-Technologies Sdn. Bhd.
7 * Copyright (c) 2024 Vogl Electronic GmbH
8 *
9 * SPDX-License-Identifier: Apache-2.0
10 */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16 #include <zephyr/data/json.h>
17 #include <zephyr/drivers/flash.h>
18 #include <zephyr/kernel.h>
19 #include <zephyr/logging/log.h>
20 #include <zephyr/logging/log_ctrl.h>
21 #include <zephyr/mgmt/hawkbit/hawkbit.h>
22 #include <zephyr/mgmt/hawkbit/config.h>
23 #include <zephyr/mgmt/hawkbit/event.h>
24 #include <zephyr/net/http/client.h>
25 #include <zephyr/net/net_ip.h>
26 #include <zephyr/net/net_mgmt.h>
27 #include <zephyr/net/socket.h>
28 #include <zephyr/settings/settings.h>
29 #include <zephyr/smf.h>
30 #include <zephyr/storage/flash_map.h>
31 #include <zephyr/sys/reboot.h>
32
33 #include <bootutil/bootutil_public.h>
34
35 #include "hawkbit_device.h"
36 #include "hawkbit_firmware.h"
37 #include "hawkbit_priv.h"
38
39 LOG_MODULE_REGISTER(hawkbit, CONFIG_HAWKBIT_LOG_LEVEL);
40
41 #define RECV_BUFFER_SIZE 640
42 #define URL_BUFFER_SIZE 300
43 #define SHA256_HASH_SIZE 32
44 #define RESPONSE_BUFFER_SIZE 1100
45 #define DDI_SECURITY_TOKEN_SIZE 32
46 #define RANGE_HEADER_SIZE 50
47 #define HAWKBIT_RECV_TIMEOUT (300 * MSEC_PER_SEC)
48 #define HAWKBIT_SET_SERVER_TIMEOUT K_MSEC(300)
49
50 #define HAWKBIT_JSON_URL "/" CONFIG_HAWKBIT_TENANT "/controller/v1"
51
52 #define HTTP_HEADER_CONTENT_TYPE_JSON "application/json;charset=UTF-8"
53
54 #define SLOT1_LABEL slot1_partition
55 #define SLOT1_SIZE FIXED_PARTITION_SIZE(SLOT1_LABEL)
56
57 static uint32_t poll_sleep = (CONFIG_HAWKBIT_POLL_INTERVAL * SEC_PER_MIN);
58
59 static bool hawkbit_initialized;
60
61 #ifndef CONFIG_HAWKBIT_DDI_NO_SECURITY
62
63 #ifdef CONFIG_HAWKBIT_DDI_GATEWAY_SECURITY
64 #define AUTH_HEADER_START "Authorization: GatewayToken "
65 #else
66 #define AUTH_HEADER_START "Authorization: TargetToken "
67 #endif /* CONFIG_HAWKBIT_DDI_GATEWAY_SECURITY */
68
69 #ifdef CONFIG_HAWKBIT_SET_SETTINGS_RUNTIME
70 #define AUTH_HEADER_FULL AUTH_HEADER_START "%s" HTTP_CRLF
71 #else
72 #define AUTH_HEADER_FULL AUTH_HEADER_START CONFIG_HAWKBIT_DDI_SECURITY_TOKEN HTTP_CRLF
73 #endif /* CONFIG_HAWKBIT_SET_SETTINGS_RUNTIME */
74
75 #endif /* CONFIG_HAWKBIT_DDI_NO_SECURITY */
76
77 #ifdef CONFIG_DNS_RESOLVER_MAX_QUERY_LEN
78 #define SERVER_ADDR_LEN CONFIG_DNS_RESOLVER_MAX_QUERY_LEN
79 #elif defined(CONFIG_NET_IPV6)
80 #define SERVER_ADDR_LEN INET6_ADDRSTRLEN
81 #else
82 #define SERVER_ADDR_LEN INET_ADDRSTRLEN
83 #endif
84
85 static struct hawkbit_config {
86 int32_t action_id;
87 #ifdef CONFIG_HAWKBIT_SET_SETTINGS_RUNTIME
88 char server_addr[SERVER_ADDR_LEN + 1];
89 #ifdef CONFIG_HAWKBIT_USE_DOMAIN_NAME
90 char server_domain[CONFIG_HAWKBIT_DOMAIN_NAME_MAX_LEN + 1];
91 #endif
92 char server_port[sizeof(STRINGIFY(__UINT16_MAX__))];
93 #ifndef CONFIG_HAWKBIT_DDI_NO_SECURITY
94 char ddi_security_token[DDI_SECURITY_TOKEN_SIZE + 1];
95 #endif
96 #ifdef CONFIG_HAWKBIT_USE_DYNAMIC_CERT_TAG
97 sec_tag_t tls_tag;
98 #endif
99 #endif /* CONFIG_HAWKBIT_SET_SETTINGS_RUNTIME */
100 } hb_cfg;
101
102 #ifdef CONFIG_HAWKBIT_SET_SETTINGS_RUNTIME
103 #ifdef CONFIG_HAWKBIT_USE_DOMAIN_NAME
104 #define HAWKBIT_SERVER_DOMAIN hb_cfg.server_domain
105 #else
106 #define HAWKBIT_SERVER_DOMAIN hb_cfg.server_addr
107 #endif /* CONFIG_HAWKBIT_USE_DOMAIN_NAME */
108 #define HAWKBIT_SERVER_ADDR hb_cfg.server_addr
109 #define HAWKBIT_PORT hb_cfg.server_port
110 #define HAWKBIT_PORT_INT atoi(hb_cfg.server_port)
111 #else
112 #define HAWKBIT_SERVER_ADDR CONFIG_HAWKBIT_SERVER
113 #define HAWKBIT_SERVER_DOMAIN CONFIG_HAWKBIT_SERVER
114 #define HAWKBIT_PORT STRINGIFY(CONFIG_HAWKBIT_PORT)
115 #define HAWKBIT_PORT_INT CONFIG_HAWKBIT_PORT
116 #endif /* CONFIG_HAWKBIT_SET_SETTINGS_RUNTIME */
117
118 #ifdef CONFIG_HAWKBIT_DDI_NO_SECURITY
119 #define HAWKBIT_DDI_SECURITY_TOKEN NULL
120 #elif defined(CONFIG_HAWKBIT_SET_SETTINGS_RUNTIME)
121 #define HAWKBIT_DDI_SECURITY_TOKEN hb_cfg.ddi_security_token
122 #else
123 #define HAWKBIT_DDI_SECURITY_TOKEN CONFIG_HAWKBIT_DDI_SECURITY_TOKEN
124 #endif /* CONFIG_HAWKBIT_DDI_NO_SECURITY */
125
126 #ifdef CONFIG_HAWKBIT_USE_DYNAMIC_CERT_TAG
127 #define HAWKBIT_CERT_TAG hb_cfg.tls_tag
128 #elif defined(HAWKBIT_USE_STATIC_CERT_TAG)
129 #define HAWKBIT_CERT_TAG CONFIG_HAWKBIT_STATIC_CERT_TAG
130 #else
131 #define HAWKBIT_CERT_TAG 0
132 #endif /* CONFIG_HAWKBIT_USE_DYNAMIC_CERT_TAG */
133
134 struct hawkbit_download {
135 size_t downloaded_size;
136 size_t http_content_size;
137 uint8_t file_hash[SHA256_HASH_SIZE];
138 int32_t file_size;
139 };
140
141 union hawkbit_results {
142 struct hawkbit_dep_res dep;
143 struct hawkbit_ctl_res base;
144 };
145
146 struct hawkbit_context {
147 int sock;
148 uint8_t *response_data;
149 size_t response_data_size;
150 int32_t json_action_id;
151 struct hawkbit_download dl;
152 struct flash_img_context flash_ctx;
153 enum hawkbit_response code_status;
154 bool final_data_received;
155 enum hawkbit_http_request type;
156 union hawkbit_results results;
157 };
158
159 struct s_object {
160 struct smf_ctx ctx;
161 struct hawkbit_context hb_context;
162 char device_id[DEVICE_ID_HEX_MAX_SIZE];
163 };
164
165 static const struct smf_state hawkbit_states[];
166
167 enum hawkbit_state {
168 S_HAWKBIT_START,
169 S_HAWKBIT_HTTP,
170 S_HAWKBIT_PROBE,
171 S_HAWKBIT_CONFIG_DEVICE,
172 S_HAWKBIT_CANCEL,
173 S_HAWKBIT_PROBE_DEPLOYMENT_BASE,
174 S_HAWKBIT_REPORT,
175 S_HAWKBIT_DOWNLOAD,
176 S_HAWKBIT_TERMINATE,
177 };
178
179 int hawkbit_default_config_data_cb(const char *device_id, uint8_t *buffer,
180 const size_t buffer_size);
181
182 static hawkbit_config_device_data_cb_handler_t hawkbit_config_device_data_cb_handler =
183 hawkbit_default_config_data_cb;
184
185 K_SEM_DEFINE(probe_sem, 1, 1);
186
187 #ifdef CONFIG_HAWKBIT_EVENT_CALLBACKS
188 static sys_slist_t event_callbacks = SYS_SLIST_STATIC_INIT(&event_callbacks);
189 #endif
190
191 static const struct json_obj_descr json_href_descr[] = {
192 JSON_OBJ_DESCR_PRIM(struct hawkbit_href, href, JSON_TOK_STRING),
193 };
194
195 static const struct json_obj_descr json_status_result_descr[] = {
196 JSON_OBJ_DESCR_PRIM(struct hawkbit_status_result, finished, JSON_TOK_STRING),
197 };
198
199 static const struct json_obj_descr json_status_descr[] = {
200 JSON_OBJ_DESCR_PRIM(struct hawkbit_status, execution, JSON_TOK_STRING),
201 JSON_OBJ_DESCR_OBJECT(struct hawkbit_status, result, json_status_result_descr),
202 };
203
204 static const struct json_obj_descr json_ctl_res_sleep_descr[] = {
205 JSON_OBJ_DESCR_PRIM(struct hawkbit_ctl_res_sleep, sleep, JSON_TOK_STRING),
206 };
207
208 static const struct json_obj_descr json_ctl_res_polling_descr[] = {
209 JSON_OBJ_DESCR_OBJECT(struct hawkbit_ctl_res_polling, polling, json_ctl_res_sleep_descr),
210 };
211
212 static const struct json_obj_descr json_ctl_res_links_descr[] = {
213 JSON_OBJ_DESCR_OBJECT(struct hawkbit_ctl_res_links, deploymentBase, json_href_descr),
214 JSON_OBJ_DESCR_OBJECT(struct hawkbit_ctl_res_links, cancelAction, json_href_descr),
215 JSON_OBJ_DESCR_OBJECT(struct hawkbit_ctl_res_links, configData, json_href_descr),
216 };
217
218 static const struct json_obj_descr json_ctl_res_descr[] = {
219 JSON_OBJ_DESCR_OBJECT(struct hawkbit_ctl_res, config, json_ctl_res_polling_descr),
220 JSON_OBJ_DESCR_OBJECT(struct hawkbit_ctl_res, _links, json_ctl_res_links_descr),
221 };
222
223 static const struct json_obj_descr json_cfg_data_descr[] = {
224 JSON_OBJ_DESCR_PRIM(struct hawkbit_cfg_data, VIN, JSON_TOK_STRING),
225 };
226
227 static const struct json_obj_descr json_cfg_descr[] = {
228 JSON_OBJ_DESCR_PRIM(struct hawkbit_cfg, mode, JSON_TOK_STRING),
229 JSON_OBJ_DESCR_OBJECT(struct hawkbit_cfg, data, json_cfg_data_descr),
230 };
231
232 static const struct json_obj_descr json_cancel_descr[] = {
233 JSON_OBJ_DESCR_OBJECT(struct hawkbit_cancel, status, json_status_descr),
234 };
235
236 static const struct json_obj_descr json_dep_res_hashes_descr[] = {
237 JSON_OBJ_DESCR_PRIM(struct hawkbit_dep_res_hashes, sha1, JSON_TOK_STRING),
238 JSON_OBJ_DESCR_PRIM(struct hawkbit_dep_res_hashes, md5, JSON_TOK_STRING),
239 JSON_OBJ_DESCR_PRIM(struct hawkbit_dep_res_hashes, sha256, JSON_TOK_STRING),
240 };
241
242 static const struct json_obj_descr json_dep_res_links_descr[] = {
243 JSON_OBJ_DESCR_OBJECT_NAMED(struct hawkbit_dep_res_links, "download-http", download_http,
244 json_href_descr),
245 JSON_OBJ_DESCR_OBJECT_NAMED(struct hawkbit_dep_res_links, "md5sum-http", md5sum_http,
246 json_href_descr),
247 };
248
249 static const struct json_obj_descr json_dep_res_arts_descr[] = {
250 JSON_OBJ_DESCR_PRIM(struct hawkbit_dep_res_arts, filename, JSON_TOK_STRING),
251 JSON_OBJ_DESCR_OBJECT(struct hawkbit_dep_res_arts, hashes, json_dep_res_hashes_descr),
252 JSON_OBJ_DESCR_PRIM(struct hawkbit_dep_res_arts, size, JSON_TOK_NUMBER),
253 JSON_OBJ_DESCR_OBJECT(struct hawkbit_dep_res_arts, _links, json_dep_res_links_descr),
254 };
255
256 static const struct json_obj_descr json_dep_res_chunk_descr[] = {
257 JSON_OBJ_DESCR_PRIM(struct hawkbit_dep_res_chunk, part, JSON_TOK_STRING),
258 JSON_OBJ_DESCR_PRIM(struct hawkbit_dep_res_chunk, version, JSON_TOK_STRING),
259 JSON_OBJ_DESCR_PRIM(struct hawkbit_dep_res_chunk, name, JSON_TOK_STRING),
260 JSON_OBJ_DESCR_OBJ_ARRAY(struct hawkbit_dep_res_chunk, artifacts,
261 HAWKBIT_DEP_MAX_CHUNK_ARTS, num_artifacts, json_dep_res_arts_descr,
262 ARRAY_SIZE(json_dep_res_arts_descr)),
263 };
264
265 static const struct json_obj_descr json_dep_res_deploy_descr[] = {
266 JSON_OBJ_DESCR_PRIM(struct hawkbit_dep_res_deploy, download, JSON_TOK_STRING),
267 JSON_OBJ_DESCR_PRIM(struct hawkbit_dep_res_deploy, update, JSON_TOK_STRING),
268 JSON_OBJ_DESCR_OBJ_ARRAY(struct hawkbit_dep_res_deploy, chunks, HAWKBIT_DEP_MAX_CHUNKS,
269 num_chunks, json_dep_res_chunk_descr,
270 ARRAY_SIZE(json_dep_res_chunk_descr)),
271 };
272
273 static const struct json_obj_descr json_dep_res_descr[] = {
274 JSON_OBJ_DESCR_PRIM(struct hawkbit_dep_res, id, JSON_TOK_STRING),
275 JSON_OBJ_DESCR_OBJECT(struct hawkbit_dep_res, deployment, json_dep_res_deploy_descr),
276 };
277
278 static const struct json_obj_descr json_dep_fbk_descr[] = {
279 JSON_OBJ_DESCR_OBJECT(struct hawkbit_dep_fbk, status, json_status_descr),
280 };
281
hawkbit_settings_set(const char * name,size_t len,settings_read_cb read_cb,void * cb_arg)282 static int hawkbit_settings_set(const char *name, size_t len, settings_read_cb read_cb,
283 void *cb_arg)
284 {
285 const char *next;
286 int rc;
287
288 if (settings_name_steq(name, "action_id", &next) && !next) {
289 if (len != sizeof(hb_cfg.action_id)) {
290 return -EINVAL;
291 }
292
293 rc = read_cb(cb_arg, &hb_cfg.action_id, sizeof(hb_cfg.action_id));
294 LOG_DBG("<%s> = %d", "hawkbit/action_id", hb_cfg.action_id);
295 if (rc >= 0) {
296 return 0;
297 }
298
299 return rc;
300 }
301
302 #ifdef CONFIG_HAWKBIT_SET_SETTINGS_RUNTIME
303 if (settings_name_steq(name, "server_addr", &next) && !next) {
304 rc = read_cb(cb_arg, &hb_cfg.server_addr, MIN(len, sizeof(hb_cfg.server_addr)));
305 if (strnlen(hb_cfg.server_addr, sizeof(hb_cfg.server_addr)) ==
306 sizeof(hb_cfg.server_addr)) {
307 memset(hb_cfg.server_addr, 0, sizeof(hb_cfg.server_addr));
308 return -EINVAL;
309 }
310 LOG_DBG("<%s> = %s", "hawkbit/server_addr", hb_cfg.server_addr);
311 if (rc >= 0) {
312 return 0;
313 }
314
315 return rc;
316 }
317
318 #ifdef CONFIG_HAWKBIT_USE_DOMAIN_NAME
319 if (settings_name_steq(name, "server_domain", &next) && !next) {
320 if (len != sizeof(hb_cfg.server_domain)) {
321 return -EINVAL;
322 }
323
324 rc = read_cb(cb_arg, &hb_cfg.server_domain, sizeof(hb_cfg.server_domain));
325 LOG_DBG("<%s> = %s", "hawkbit/server_domain", hb_cfg.server_domain);
326 if (rc >= 0) {
327 return 0;
328 }
329
330 return rc;
331 }
332 #endif /* CONFIG_HAWKBIT_USE_DOMAIN_NAME */
333
334 if (settings_name_steq(name, "server_port", &next) && !next) {
335 if (len != sizeof(uint16_t)) {
336 return -EINVAL;
337 }
338
339 uint16_t hawkbit_port = atoi(hb_cfg.server_port);
340
341 rc = read_cb(cb_arg, &hawkbit_port, sizeof(hawkbit_port));
342 if (hawkbit_port != atoi(hb_cfg.server_port)) {
343 snprintf(hb_cfg.server_port, sizeof(hb_cfg.server_port), "%u",
344 hawkbit_port);
345 }
346 LOG_DBG("<%s> = %s", "hawkbit/server_port", hb_cfg.server_port);
347 if (rc >= 0) {
348 return 0;
349 }
350
351 return rc;
352 }
353
354 if (settings_name_steq(name, "ddi_token", &next) && !next) {
355 #ifdef CONFIG_HAWKBIT_DDI_NO_SECURITY
356 rc = read_cb(cb_arg, NULL, 0);
357 #else
358 if (len != sizeof(hb_cfg.ddi_security_token)) {
359 return -EINVAL;
360 }
361
362 rc = read_cb(cb_arg, &hb_cfg.ddi_security_token, sizeof(hb_cfg.ddi_security_token));
363 LOG_DBG("<%s> = %s", "hawkbit/ddi_token", hb_cfg.ddi_security_token);
364 if (rc >= 0) {
365 return 0;
366 }
367 #endif /* CONFIG_HAWKBIT_DDI_NO_SECURITY */
368 return rc;
369 }
370 #else /* CONFIG_HAWKBIT_SET_SETTINGS_RUNTIME */
371 if (settings_name_steq(name, "server_addr", NULL) ||
372 #ifdef CONFIG_HAWKBIT_USE_DOMAIN_NAME
373 settings_name_steq(name, "server_domain", NULL) ||
374 #endif /* CONFIG_HAWKBIT_USE_DOMAIN_NAME */
375 settings_name_steq(name, "server_port", NULL) ||
376 settings_name_steq(name, "ddi_token", NULL)) {
377 rc = read_cb(cb_arg, NULL, 0);
378 return 0;
379 }
380 #endif /* CONFIG_HAWKBIT_SET_SETTINGS_RUNTIME */
381 /* This is to omit the error message, as that is fetched in stream_flash_progress_load()
382 * and we don't need to get it here.
383 */
384 if (IS_ENABLED(CONFIG_HAWKBIT_SAVE_PROGRESS) &&
385 settings_name_steq(name, "flash_progress", NULL)) {
386 return 0;
387 }
388
389 return -ENOENT;
390 }
391
hawkbit_settings_export(int (* cb)(const char * name,const void * value,size_t val_len))392 static int hawkbit_settings_export(int (*cb)(const char *name, const void *value, size_t val_len))
393 {
394 LOG_DBG("export hawkbit settings");
395 (void)cb("hawkbit/action_id", &hb_cfg.action_id, sizeof(hb_cfg.action_id));
396 #ifdef CONFIG_HAWKBIT_SET_SETTINGS_RUNTIME
397 (void)cb("hawkbit/server_addr", &hb_cfg.server_addr, strlen(hb_cfg.server_addr) + 1);
398 #ifdef CONFIG_HAWKBIT_USE_DOMAIN_NAME
399 (void)cb("hawkbit/server_domain", &hb_cfg.server_domain, sizeof(hb_cfg.server_domain));
400 #endif /* CONFIG_HAWKBIT_USE_DOMAIN_NAME */
401 uint16_t hawkbit_port = atoi(hb_cfg.server_port);
402 (void)cb("hawkbit/server_port", &hawkbit_port, sizeof(hawkbit_port));
403 #ifndef CONFIG_HAWKBIT_DDI_NO_SECURITY
404 (void)cb("hawkbit/ddi_token", &hb_cfg.ddi_security_token,
405 sizeof(hb_cfg.ddi_security_token));
406 #endif /* CONFIG_HAWKBIT_DDI_NO_SECURITY */
407 #endif /* CONFIG_HAWKBIT_SET_SETTINGS_RUNTIME */
408 return 0;
409 }
410
411 SETTINGS_STATIC_HANDLER_DEFINE(hawkbit, "hawkbit", NULL, hawkbit_settings_set, NULL,
412 hawkbit_settings_export);
413
hawkbit_event_raise(enum hawkbit_event_type event)414 static void hawkbit_event_raise(enum hawkbit_event_type event)
415 {
416 #ifdef CONFIG_HAWKBIT_EVENT_CALLBACKS
417 struct hawkbit_event_callback *cb, *tmp;
418
419 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&event_callbacks, cb, tmp, node) {
420 if (cb->event == event && cb->handler) {
421 cb->handler(cb, event);
422 }
423 }
424 #endif
425 }
426
427 #ifdef CONFIG_HAWKBIT_EVENT_CALLBACKS
hawkbit_event_add_callback(struct hawkbit_event_callback * cb)428 int hawkbit_event_add_callback(struct hawkbit_event_callback *cb)
429 {
430 int ret = 0;
431
432 if (cb == NULL || cb->handler == NULL) {
433 return -EINVAL;
434 }
435
436 ret = k_sem_take(&probe_sem, K_FOREVER);
437 if (ret == 0) {
438 sys_slist_prepend(&event_callbacks, &cb->node);
439 k_sem_give(&probe_sem);
440 }
441 return ret;
442 }
443
hawkbit_event_remove_callback(struct hawkbit_event_callback * cb)444 int hawkbit_event_remove_callback(struct hawkbit_event_callback *cb)
445 {
446 int ret = 0;
447
448 if (cb == NULL || cb->handler == NULL) {
449 return -EINVAL;
450 }
451
452 ret = k_sem_take(&probe_sem, K_FOREVER);
453 if (ret == 0) {
454 if (!sys_slist_find_and_remove(&event_callbacks, &cb->node)) {
455 ret = -EINVAL;
456 }
457 k_sem_give(&probe_sem);
458 }
459
460 return ret;
461 }
462 #endif /* CONFIG_HAWKBIT_EVENT_CALLBACKS */
463
start_http_client(int * hb_sock)464 static bool start_http_client(int *hb_sock)
465 {
466 int ret = -1;
467 struct zsock_addrinfo *addr;
468 struct zsock_addrinfo hints = {0};
469 int resolve_attempts = 10;
470 int protocol = IS_ENABLED(CONFIG_HAWKBIT_USE_TLS) ? IPPROTO_TLS_1_2 : IPPROTO_TCP;
471
472 if (IS_ENABLED(CONFIG_NET_IPV6)) {
473 hints.ai_family = AF_INET6;
474 hints.ai_socktype = SOCK_STREAM;
475 } else if (IS_ENABLED(CONFIG_NET_IPV4)) {
476 hints.ai_family = AF_INET;
477 hints.ai_socktype = SOCK_STREAM;
478 }
479
480 while (resolve_attempts--) {
481 ret = zsock_getaddrinfo(HAWKBIT_SERVER_ADDR, HAWKBIT_PORT, &hints, &addr);
482 if (ret == 0) {
483 break;
484 }
485
486 k_sleep(K_MSEC(1));
487 }
488
489 if (ret != 0) {
490 LOG_ERR("Failed to resolve dns: %d", ret);
491 return false;
492 }
493
494 *hb_sock = zsock_socket(addr->ai_family, SOCK_STREAM, protocol);
495 if (*hb_sock < 0) {
496 LOG_ERR("Failed to create TCP socket");
497 goto err;
498 }
499
500 #ifdef CONFIG_HAWKBIT_USE_TLS
501 sec_tag_t sec_tag_opt[] = {
502 HAWKBIT_CERT_TAG,
503 };
504
505 if (zsock_setsockopt(*hb_sock, SOL_TLS, TLS_SEC_TAG_LIST, sec_tag_opt,
506 sizeof(sec_tag_opt)) < 0) {
507 LOG_ERR("Failed to set TLS_TAG option");
508 goto err_sock;
509 }
510
511 if (zsock_setsockopt(*hb_sock, SOL_TLS, TLS_HOSTNAME, HAWKBIT_SERVER_DOMAIN,
512 sizeof(HAWKBIT_SERVER_DOMAIN)) < 0) {
513 goto err_sock;
514 }
515 #endif /* CONFIG_HAWKBIT_USE_TLS */
516
517 if (zsock_connect(*hb_sock, addr->ai_addr, addr->ai_addrlen) < 0) {
518 LOG_ERR("Failed to connect to server");
519 goto err_sock;
520 }
521
522 zsock_freeaddrinfo(addr);
523 return true;
524
525 err_sock:
526 zsock_close(*hb_sock);
527 err:
528 zsock_freeaddrinfo(addr);
529 return false;
530 }
531
cleanup_connection(int * hb_sock)532 static void cleanup_connection(int *hb_sock)
533 {
534 if (zsock_close(*hb_sock) < 0) {
535 LOG_ERR("Failed to close the socket");
536 }
537 }
538
hawkbit_time2sec(const char * s)539 static int hawkbit_time2sec(const char *s)
540 {
541 int sec;
542
543 /* Time: HH:MM:SS */
544 sec = strtol(s, NULL, 10) * (60 * 60);
545 sec += strtol(s + 3, NULL, 10) * 60;
546 sec += strtol(s + 6, NULL, 10);
547
548 if (sec < 0) {
549 return -1;
550 } else {
551 return sec;
552 }
553 }
554
hawkbit_status_finished(enum hawkbit_status_fini f)555 static const char *hawkbit_status_finished(enum hawkbit_status_fini f)
556 {
557 switch (f) {
558 case HAWKBIT_STATUS_FINISHED_SUCCESS:
559 return "success";
560 case HAWKBIT_STATUS_FINISHED_FAILURE:
561 return "failure";
562 case HAWKBIT_STATUS_FINISHED_NONE:
563 return "none";
564 default:
565 LOG_ERR("%d is invalid", (int)f);
566 return NULL;
567 }
568 }
569
hawkbit_status_execution(enum hawkbit_status_exec e)570 static const char *hawkbit_status_execution(enum hawkbit_status_exec e)
571 {
572 switch (e) {
573 case HAWKBIT_STATUS_EXEC_CLOSED:
574 return "closed";
575 case HAWKBIT_STATUS_EXEC_PROCEEDING:
576 return "proceeding";
577 case HAWKBIT_STATUS_EXEC_CANCELED:
578 return "canceled";
579 case HAWKBIT_STATUS_EXEC_SCHEDULED:
580 return "scheduled";
581 case HAWKBIT_STATUS_EXEC_REJECTED:
582 return "rejected";
583 case HAWKBIT_STATUS_EXEC_RESUMED:
584 return "resumed";
585 case HAWKBIT_STATUS_EXEC_NONE:
586 return "none";
587 default:
588 LOG_ERR("%d is invalid", (int)e);
589 return NULL;
590 }
591 }
592
hawkbit_device_acid_update(int32_t new_value)593 static int hawkbit_device_acid_update(int32_t new_value)
594 {
595 int ret;
596 hb_cfg.action_id = new_value;
597
598 ret = settings_save_one("hawkbit/action_id", &hb_cfg.action_id, sizeof(hb_cfg.action_id));
599 if (ret < 0) {
600 LOG_ERR("Failed to write device id: %d", ret);
601 return -EIO;
602 }
603
604 return 0;
605 }
606
hawkbit_reset_action_id(void)607 int hawkbit_reset_action_id(void)
608 {
609 int ret;
610
611 if (k_sem_take(&probe_sem, K_NO_WAIT) == 0) {
612 ret = hawkbit_device_acid_update(0);
613 k_sem_give(&probe_sem);
614 return ret;
615 }
616 return -EAGAIN;
617 }
618
hawkbit_get_action_id(void)619 int32_t hawkbit_get_action_id(void)
620 {
621 return hb_cfg.action_id;
622 }
623
hawkbit_get_poll_interval(void)624 uint32_t hawkbit_get_poll_interval(void)
625 {
626 return poll_sleep;
627 }
628
629 /*
630 * Update sleep interval, based on results from hawkBit base polling
631 * resource
632 */
hawkbit_update_sleep(struct hawkbit_ctl_res * hawkbit_res)633 static void hawkbit_update_sleep(struct hawkbit_ctl_res *hawkbit_res)
634 {
635 int sleep_time;
636 const char *sleep = hawkbit_res->config.polling.sleep;
637
638 if (strlen(sleep) != HAWKBIT_SLEEP_LENGTH) {
639 LOG_ERR("Invalid poll sleep: %s", sleep);
640 } else {
641 sleep_time = hawkbit_time2sec(sleep);
642 if (sleep_time > 0 && poll_sleep != sleep_time) {
643 LOG_DBG("New poll sleep %d seconds", sleep_time);
644 poll_sleep = (uint32_t)sleep_time;
645 }
646 }
647 }
648
hawkbit_get_url(const char * href)649 static char *hawkbit_get_url(const char *href)
650 {
651 char *helper;
652
653 helper = strstr(href, "//");
654 if (helper != NULL) {
655 helper = strstr(helper + 2u, "/");
656 }
657
658 if (!helper) {
659 LOG_ERR("Unexpected href format: %s", helper);
660 return NULL;
661 }
662 return helper;
663 }
664
665 /*
666 * Find URL component for the device cancel action id
667 */
hawkbit_find_cancel_action_id(struct hawkbit_ctl_res * res,int32_t * cancel_action_id)668 static int hawkbit_find_cancel_action_id(struct hawkbit_ctl_res *res, int32_t *cancel_action_id)
669 {
670 char *helper;
671
672 helper = strstr(res->_links.cancelAction.href, "cancelAction/");
673 if (!helper) {
674 /* A badly formatted cancel base is a server error */
675 LOG_ERR("Missing %s/ in href %s", "cancelAction", res->_links.cancelAction.href);
676 return -EINVAL;
677 }
678
679 helper += sizeof("cancelAction/") - 1;
680
681 *cancel_action_id = strtol(helper, NULL, 10);
682 if (*cancel_action_id <= 0) {
683 LOG_ERR("Invalid action_id: %d", *cancel_action_id);
684 return -EINVAL;
685 }
686
687 return 0;
688 }
689
hawkbit_deployment_get_action_id(struct hawkbit_dep_res * res,int32_t * action_id)690 static int hawkbit_deployment_get_action_id(struct hawkbit_dep_res *res, int32_t *action_id)
691 {
692 int32_t id;
693
694 id = strtol(res->id, NULL, 10);
695 if (id <= 0) {
696 LOG_ERR("Invalid action_id: %d", id);
697 return -EINVAL;
698 }
699
700 *action_id = id;
701
702 return 0;
703 }
704
705 /*
706 * Find URL component for this device's deployment operations
707 * resource.
708 */
hawkbit_parse_deployment(struct hawkbit_context * hb_context,struct hawkbit_dep_res * res,char ** download_http)709 static int hawkbit_parse_deployment(struct hawkbit_context *hb_context, struct hawkbit_dep_res *res,
710 char **download_http)
711 {
712 const char *href;
713 struct hawkbit_dep_res_chunk *chunk;
714 size_t num_chunks, num_artifacts;
715 struct hawkbit_dep_res_arts *artifact;
716
717 num_chunks = res->deployment.num_chunks;
718 if (num_chunks != 1) {
719 LOG_ERR("Expecting 1 chunk (got %d)", num_chunks);
720 return -ENOSPC;
721 }
722
723 chunk = &res->deployment.chunks[0];
724 if (strcmp("bApp", chunk->part)) {
725 LOG_ERR("Only part 'bApp' is supported; got %s", chunk->part);
726 return -EINVAL;
727 }
728
729 num_artifacts = chunk->num_artifacts;
730 if (num_artifacts != 1) {
731 LOG_ERR("Expecting 1 artifact (got %d)", num_artifacts);
732 return -EINVAL;
733 }
734
735 artifact = &chunk->artifacts[0];
736 if (hex2bin(artifact->hashes.sha256, SHA256_HASH_SIZE << 1, hb_context->dl.file_hash,
737 sizeof(hb_context->dl.file_hash)) != SHA256_HASH_SIZE) {
738 return -EINVAL;
739 }
740
741 hb_context->dl.file_size = artifact->size;
742
743 if (hb_context->dl.file_size > SLOT1_SIZE) {
744 LOG_ERR("Artifact file size too big (got %d, max is %d)", hb_context->dl.file_size,
745 SLOT1_SIZE);
746 return -ENOSPC;
747 }
748
749 /*
750 * Find the download-http href.
751 */
752 href = artifact->_links.download_http.href;
753 if (!href) {
754 LOG_ERR("Missing expected %s href", "download-http");
755 return -EINVAL;
756 }
757
758 /* Success. */
759 if (download_http != NULL) {
760 *download_http = hawkbit_get_url(href);
761 if (*download_http == NULL) {
762 LOG_ERR("Failed to parse %s url", "deploymentBase");
763 return -EINVAL;
764 }
765 }
766 return 0;
767 }
768
hawkbit_dump_deployment(struct hawkbit_dep_res * d)769 static void hawkbit_dump_deployment(struct hawkbit_dep_res *d)
770 {
771 struct hawkbit_dep_res_chunk *c = &d->deployment.chunks[0];
772 struct hawkbit_dep_res_arts *a = &c->artifacts[0];
773 struct hawkbit_dep_res_links *l = &a->_links;
774
775 LOG_DBG("%s=%s", "id", d->id);
776 LOG_DBG("%s=%s", "download", d->deployment.download);
777 LOG_DBG("%s=%s", "update", d->deployment.update);
778 LOG_DBG("chunks[0].%s=%s", "part", c->part);
779 LOG_DBG("chunks[0].%s=%s", "name", c->name);
780 LOG_DBG("chunks[0].%s=%s", "version", c->version);
781 LOG_DBG("chunks[0].artifacts[0].%s=%s", "filename", a->filename);
782 LOG_DBG("chunks[0].artifacts[0].%s=%s", "hashes.sha1", a->hashes.sha1);
783 LOG_DBG("chunks[0].artifacts[0].%s=%s", "hashes.md5", a->hashes.md5);
784 LOG_DBG("chunks[0].artifacts[0].%s=%s", "hashes.sha256", a->hashes.sha256);
785 LOG_DBG("chunks[0].size=%d", a->size);
786 LOG_DBG("%s=%s", "download-http", l->download_http.href);
787 LOG_DBG("%s=%s", "md5sum-http", l->md5sum_http.href);
788 }
789
hawkbit_set_custom_data_cb(hawkbit_config_device_data_cb_handler_t cb)790 int hawkbit_set_custom_data_cb(hawkbit_config_device_data_cb_handler_t cb)
791 {
792 if (IS_ENABLED(CONFIG_HAWKBIT_CUSTOM_ATTRIBUTES)) {
793 if (cb == NULL) {
794 LOG_ERR("Invalid callback");
795 return -EINVAL;
796 }
797
798 hawkbit_config_device_data_cb_handler = cb;
799
800 return 0;
801 }
802 return -ENOTSUP;
803 }
804
hawkbit_default_config_data_cb(const char * device_id,uint8_t * buffer,const size_t buffer_size)805 int hawkbit_default_config_data_cb(const char *device_id, uint8_t *buffer,
806 const size_t buffer_size)
807 {
808 struct hawkbit_cfg cfg = {
809 .mode = "merge",
810 .data.VIN = device_id,
811 };
812
813 return json_obj_encode_buf(json_cfg_descr, ARRAY_SIZE(json_cfg_descr), &cfg, buffer,
814 buffer_size);
815 }
816
817 #ifdef CONFIG_HAWKBIT_SET_SETTINGS_RUNTIME
hawkbit_set_config(struct hawkbit_runtime_config * config)818 int hawkbit_set_config(struct hawkbit_runtime_config *config)
819 {
820 if (k_sem_take(&probe_sem, HAWKBIT_SET_SERVER_TIMEOUT) == 0) {
821 if (config->server_addr != NULL) {
822 if (strnlen(config->server_addr, sizeof(hb_cfg.server_addr)) ==
823 sizeof(hb_cfg.server_addr)) {
824 LOG_ERR("%s too long: %s", "hawkbit/server_addr",
825 config->server_addr);
826 return -EINVAL;
827 }
828 strncpy(hb_cfg.server_addr, config->server_addr,
829 sizeof(hb_cfg.server_addr));
830 LOG_DBG("configured %s: %s", "hawkbit/server_addr", hb_cfg.server_addr);
831 }
832 #ifdef CONFIG_HAWKBIT_USE_DOMAIN_NAME
833 if (config->server_domain != NULL) {
834 if (strnlen(config->server_domain, CONFIG_HAWKBIT_DOMAIN_NAME_MAX_LEN + 1)
835 > CONFIG_HAWKBIT_DOMAIN_NAME_MAX_LEN) {
836 LOG_ERR("%s too long: %s", "hawkbit/server_domain",
837 config->server_domain);
838 return -EINVAL;
839 }
840 strncpy(hb_cfg.server_domain, config->server_domain,
841 sizeof(hb_cfg.server_domain));
842 LOG_DBG("configured %s: %s", "hawkbit/server_domain",
843 hb_cfg.server_domain);
844 }
845 #endif /* CONFIG_HAWKBIT_USE_DOMAIN_NAME */
846 if (config->server_port != 0) {
847 snprintf(hb_cfg.server_port, sizeof(hb_cfg.server_port), "%u",
848 config->server_port);
849 LOG_DBG("configured %s: %s", "hawkbit/server_port", hb_cfg.server_port);
850 }
851 #ifndef CONFIG_HAWKBIT_DDI_NO_SECURITY
852 if (config->auth_token != NULL) {
853 strncpy(hb_cfg.ddi_security_token, config->auth_token,
854 sizeof(hb_cfg.ddi_security_token));
855 LOG_DBG("configured %s: %s", "hawkbit/ddi_token",
856 hb_cfg.ddi_security_token);
857 }
858 #endif /* CONFIG_HAWKBIT_DDI_NO_SECURITY */
859 #ifdef CONFIG_HAWKBIT_USE_DYNAMIC_CERT_TAG
860 if (config->tls_tag != 0) {
861 hb_cfg.tls_tag = config->tls_tag;
862 LOG_DBG("configured %s: %d", "hawkbit/tls_tag", hb_cfg.tls_tag);
863 }
864 #endif /* CONFIG_HAWKBIT_USE_DYNAMIC_CERT_TAG */
865 settings_save_subtree("hawkbit");
866 k_sem_give(&probe_sem);
867 } else {
868 LOG_WRN("failed setting config");
869 return -EAGAIN;
870 }
871
872 return 0;
873 }
874 #endif /* CONFIG_HAWKBIT_SET_SETTINGS_RUNTIME */
875
hawkbit_get_config(void)876 struct hawkbit_runtime_config hawkbit_get_config(void)
877 {
878 struct hawkbit_runtime_config config = {
879 .server_addr = HAWKBIT_SERVER_ADDR,
880 .server_port = HAWKBIT_PORT_INT,
881 .auth_token = HAWKBIT_DDI_SECURITY_TOKEN,
882 .tls_tag = HAWKBIT_CERT_TAG,
883 };
884
885 return config;
886 }
887
hawkbit_init(void)888 int hawkbit_init(void)
889 {
890 bool image_ok;
891 int ret = 0;
892
893 if (hawkbit_initialized) {
894 return 0;
895 }
896
897 ret = settings_subsys_init();
898 if (ret < 0) {
899 LOG_ERR("Failed to initialize settings subsystem: %d", ret);
900 return ret;
901 }
902
903 ret = settings_load_subtree("hawkbit");
904 if (ret < 0) {
905 LOG_ERR("Failed to load settings: %d", ret);
906 return ret;
907 }
908
909 LOG_DBG("Current action_id: %d", hb_cfg.action_id);
910
911 image_ok = boot_is_img_confirmed();
912 LOG_INF("Current image is%s confirmed", image_ok ? "" : " not");
913 if (!image_ok) {
914 ret = boot_write_img_confirmed();
915 if (ret < 0) {
916 LOG_ERR("Failed to confirm current image: %d", ret);
917 return ret;
918 }
919
920 LOG_DBG("Marked current image as OK");
921 ret = boot_erase_img_bank(flash_img_get_upload_slot());
922 if (ret < 0) {
923 LOG_ERR("Failed to erase second slot: %d", ret);
924 return ret;
925 }
926
927 hawkbit_event_raise(HAWKBIT_EVENT_CONFIRMED_CURRENT_IMAGE);
928 }
929 hawkbit_initialized = true;
930
931 return ret;
932 }
933
response_json_cb(struct http_response * rsp,enum http_final_call final_data,struct hawkbit_context * hb_context)934 static void response_json_cb(struct http_response *rsp, enum http_final_call final_data,
935 struct hawkbit_context *hb_context)
936 {
937 size_t body_len;
938 int ret;
939 uint8_t *body_data = NULL, *rsp_tmp = NULL;
940
941 if (hb_context->dl.http_content_size == 0) {
942 hb_context->dl.http_content_size = rsp->content_length;
943 }
944
945 if (rsp->body_found) {
946 body_data = rsp->body_frag_start;
947 body_len = rsp->body_frag_len;
948
949 if ((hb_context->dl.downloaded_size + body_len) > hb_context->response_data_size) {
950 hb_context->response_data_size = hb_context->dl.downloaded_size + body_len;
951 rsp_tmp = k_realloc(hb_context->response_data,
952 hb_context->response_data_size);
953 if (rsp_tmp == NULL) {
954 LOG_ERR("Failed to realloc memory");
955 hb_context->code_status = HAWKBIT_ALLOC_ERROR;
956 return;
957 }
958
959 hb_context->response_data = rsp_tmp;
960 }
961 strncpy(hb_context->response_data + hb_context->dl.downloaded_size, body_data,
962 body_len);
963 hb_context->dl.downloaded_size += body_len;
964 }
965
966 if (final_data == HTTP_DATA_FINAL) {
967 if (hb_context->dl.http_content_size != hb_context->dl.downloaded_size) {
968 LOG_ERR("HTTP response len mismatch, expected %d, got %d",
969 hb_context->dl.http_content_size, hb_context->dl.downloaded_size);
970 hb_context->code_status = HAWKBIT_METADATA_ERROR;
971 return;
972 }
973
974 hb_context->response_data[hb_context->dl.downloaded_size] = '\0';
975 memset(&hb_context->results, 0, sizeof(hb_context->results));
976 if (hb_context->type == HAWKBIT_PROBE) {
977 ret = json_obj_parse(hb_context->response_data,
978 hb_context->dl.downloaded_size, json_ctl_res_descr,
979 ARRAY_SIZE(json_ctl_res_descr),
980 &hb_context->results.base);
981 if (ret < 0) {
982 LOG_ERR("JSON parse error (%s): %d", "HAWKBIT_PROBE", ret);
983 hb_context->code_status = HAWKBIT_METADATA_ERROR;
984 }
985 } else {
986 ret = json_obj_parse(hb_context->response_data,
987 hb_context->dl.downloaded_size, json_dep_res_descr,
988 ARRAY_SIZE(json_dep_res_descr),
989 &hb_context->results.dep);
990 if (ret < 0) {
991 LOG_ERR("JSON parse error (%s): %d", "deploymentBase", ret);
992 hb_context->code_status = HAWKBIT_METADATA_ERROR;
993 }
994 }
995 }
996 }
997
response_download_cb(struct http_response * rsp,enum http_final_call final_data,struct hawkbit_context * hb_context)998 static void response_download_cb(struct http_response *rsp, enum http_final_call final_data,
999 struct hawkbit_context *hb_context)
1000 {
1001 size_t body_len;
1002 int ret, downloaded;
1003 uint8_t *body_data = NULL;
1004 static uint8_t download_progress;
1005
1006 if (hb_context->dl.http_content_size == 0) {
1007 hb_context->dl.http_content_size = rsp->content_length;
1008 download_progress = 0;
1009 if (IS_ENABLED(CONFIG_HAWKBIT_SAVE_PROGRESS) && rsp->http_status_code != 206) {
1010 hb_context->flash_ctx.stream.bytes_written = 0;
1011 }
1012 }
1013
1014 if (!rsp->body_found) {
1015 return;
1016 }
1017
1018 body_data = rsp->body_frag_start;
1019 body_len = rsp->body_frag_len;
1020
1021 ret = flash_img_buffered_write(&hb_context->flash_ctx, body_data, body_len,
1022 final_data == HTTP_DATA_FINAL);
1023 if (ret < 0) {
1024 LOG_ERR("Failed to write flash: %d", ret);
1025 hb_context->code_status = HAWKBIT_DOWNLOAD_ERROR;
1026 return;
1027 }
1028
1029 #if defined CONFIG_HAWKBIT_SAVE_PROGRESS && IS_EQ(CONFIG_HAWKBIT_SAVE_PROGRESS_INTERVAL, 0)
1030 stream_flash_progress_save(&hb_context->flash_ctx.stream, "hawkbit/flash_progress");
1031 #endif
1032
1033 hb_context->dl.downloaded_size = flash_img_bytes_written(&hb_context->flash_ctx);
1034
1035 downloaded = hb_context->dl.downloaded_size * 100 / hb_context->dl.file_size;
1036
1037 if (downloaded != download_progress) {
1038 #if defined CONFIG_HAWKBIT_SAVE_PROGRESS && !IS_EQ(CONFIG_HAWKBIT_SAVE_PROGRESS_INTERVAL, 0)
1039 if ((downloaded / CONFIG_HAWKBIT_SAVE_PROGRESS_INTERVAL) >
1040 (download_progress / CONFIG_HAWKBIT_SAVE_PROGRESS_INTERVAL)) {
1041 stream_flash_progress_save(&hb_context->flash_ctx.stream,
1042 "hawkbit/flash_progress");
1043 }
1044 #endif
1045 download_progress = downloaded;
1046 LOG_DBG("Downloaded: %u%% (%u / %u)", download_progress,
1047 hb_context->dl.downloaded_size, hb_context->dl.file_size);
1048 }
1049
1050 if (final_data == HTTP_DATA_FINAL) {
1051 hb_context->final_data_received = true;
1052 }
1053 }
1054
response_cb(struct http_response * rsp,enum http_final_call final_data,void * userdata)1055 static int response_cb(struct http_response *rsp, enum http_final_call final_data, void *userdata)
1056 {
1057 struct hawkbit_context *hb_context = userdata;
1058
1059 if (!IN_RANGE(rsp->http_status_code, 200, 299)) {
1060 LOG_ERR("HTTP request denied: %d", rsp->http_status_code);
1061 if (rsp->http_status_code == 401 || rsp->http_status_code == 403) {
1062 hb_context->code_status = HAWKBIT_PERMISSION_ERROR;
1063 } else {
1064 hb_context->code_status = HAWKBIT_METADATA_ERROR;
1065 }
1066 return 0;
1067 }
1068
1069 switch (hb_context->type) {
1070 case HAWKBIT_PROBE:
1071 case HAWKBIT_PROBE_DEPLOYMENT_BASE:
1072 response_json_cb(rsp, final_data, hb_context);
1073 break;
1074
1075 case HAWKBIT_DOWNLOAD:
1076 response_download_cb(rsp, final_data, hb_context);
1077 break;
1078
1079 default:
1080 break;
1081 }
1082
1083 return 0;
1084 }
1085
send_request(struct hawkbit_context * hb_context,enum hawkbit_http_request type,char * url_buffer,uint8_t * status_buffer)1086 static bool send_request(struct hawkbit_context *hb_context, enum hawkbit_http_request type,
1087 char *url_buffer, uint8_t *status_buffer)
1088 {
1089 int ret = 0;
1090 uint8_t recv_buf_tcp[RECV_BUFFER_SIZE] = {0};
1091 struct http_request http_req = {0};
1092 #ifndef CONFIG_HAWKBIT_DDI_NO_SECURITY
1093 #ifdef CONFIG_HAWKBIT_SET_SETTINGS_RUNTIME
1094 char header[DDI_SECURITY_TOKEN_SIZE + sizeof(AUTH_HEADER_START) + sizeof(HTTP_CRLF) - 1];
1095
1096 snprintf(header, sizeof(header), AUTH_HEADER_FULL, hb_cfg.ddi_security_token);
1097 const char *const headers[] = {header, NULL};
1098 #else
1099 static const char *const headers[] = {AUTH_HEADER_FULL, NULL};
1100 #endif /* CONFIG_HAWKBIT_SET_SETTINGS_RUNTIME */
1101 #endif /* CONFIG_HAWKBIT_DDI_NO_SECURITY */
1102
1103 http_req.url = url_buffer;
1104 http_req.host = HAWKBIT_SERVER_DOMAIN;
1105 http_req.port = HAWKBIT_PORT;
1106 http_req.protocol = "HTTP/1.1";
1107 http_req.response = response_cb;
1108 http_req.recv_buf = recv_buf_tcp;
1109 http_req.recv_buf_len = sizeof(recv_buf_tcp);
1110 #ifndef CONFIG_HAWKBIT_DDI_NO_SECURITY
1111 http_req.header_fields = (const char **)headers;
1112 #endif
1113 hb_context->final_data_received = false;
1114 hb_context->type = type;
1115
1116 switch (type) {
1117 case HAWKBIT_CONFIG_DEVICE:
1118 /*
1119 * Feedback channel for the config data action
1120 * PUT: /{tenant}/controller/v1/{controllerId}/configData
1121 */
1122 http_req.method = HTTP_PUT;
1123 http_req.content_type_value = HTTP_HEADER_CONTENT_TYPE_JSON;
1124 http_req.payload = status_buffer;
1125 http_req.payload_len = strlen(status_buffer);
1126
1127 break;
1128
1129 case HAWKBIT_CANCEL:
1130 /*
1131 * Feedback channel for cancel actions
1132 * POST: /{tenant}/controller/v1/{controllerId}/cancelAction/{actionId}/feedback
1133 */
1134 case HAWKBIT_REPORT:
1135 /*
1136 * Feedback channel for the DeploymentBase action
1137 * POST: /{tenant}/controller/v1/{controllerId}/deploymentBase/{actionId}/feedback
1138 */
1139 http_req.method = HTTP_POST;
1140 http_req.content_type_value = HTTP_HEADER_CONTENT_TYPE_JSON;
1141 http_req.payload = status_buffer;
1142 http_req.payload_len = strlen(status_buffer);
1143
1144 break;
1145
1146 case HAWKBIT_PROBE:
1147 /*
1148 * Root resource for an individual Target
1149 * GET: /{tenant}/controller/v1/{controllerId}
1150 */
1151 case HAWKBIT_PROBE_DEPLOYMENT_BASE:
1152 /*
1153 * Resource for software module (Deployment Base)
1154 * GET: /{tenant}/controller/v1/{controllerId}/deploymentBase/{actionId}
1155 */
1156
1157 http_req.method = HTTP_GET;
1158 hb_context->dl.http_content_size = 0;
1159 hb_context->dl.downloaded_size = 0;
1160
1161 break;
1162
1163 case HAWKBIT_DOWNLOAD:
1164 /*
1165 * Resource for software module (Deployment Base)
1166 * GET: /{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/
1167 * artifacts/{fileName}
1168 */
1169 http_req.method = HTTP_GET;
1170 hb_context->dl.http_content_size = 0;
1171
1172 #ifdef CONFIG_HAWKBIT_SAVE_PROGRESS
1173 hb_context->dl.downloaded_size = flash_img_bytes_written(&hb_context->flash_ctx);
1174 if (IN_RANGE(hb_context->dl.downloaded_size, 1, hb_context->dl.file_size)) {
1175 char header_range[RANGE_HEADER_SIZE] = {0};
1176
1177 snprintf(header_range, sizeof(header_range), "Range: bytes=%u-" HTTP_CRLF,
1178 hb_context->dl.downloaded_size);
1179 const char *const headers_range[] = {header_range, NULL};
1180
1181 http_req.optional_headers = (const char **)headers_range;
1182 LOG_DBG("optional header: %s", header_range);
1183 LOG_INF("Resuming download from %d bytes", hb_context->dl.downloaded_size);
1184 }
1185 #else
1186 hb_context->dl.downloaded_size = 0;
1187 #endif
1188
1189 break;
1190
1191 default:
1192 return false;
1193 }
1194
1195 ret = http_client_req(hb_context->sock, &http_req, HAWKBIT_RECV_TIMEOUT, hb_context);
1196 if (ret < 0) {
1197 LOG_ERR("Failed to send request: %d", ret);
1198 hb_context->code_status = HAWKBIT_NETWORKING_ERROR;
1199 return false;
1200 }
1201
1202 if (IN_RANGE(hb_context->code_status, HAWKBIT_NETWORKING_ERROR, HAWKBIT_ALLOC_ERROR)) {
1203 return false;
1204 }
1205
1206 return true;
1207 }
1208
hawkbit_reboot(void)1209 void hawkbit_reboot(void)
1210 {
1211 hawkbit_event_raise(HAWKBIT_EVENT_BEFORE_REBOOT);
1212 LOG_PANIC();
1213 sys_reboot(IS_ENABLED(CONFIG_HAWKBIT_REBOOT_COLD) ? SYS_REBOOT_COLD : SYS_REBOOT_WARM);
1214 }
1215
check_hawkbit_server(void)1216 static bool check_hawkbit_server(void)
1217 {
1218 if (strlen(HAWKBIT_SERVER_ADDR) == 0) {
1219 if (sizeof(CONFIG_HAWKBIT_SERVER) > 1) {
1220 hawkbit_set_server_addr(CONFIG_HAWKBIT_SERVER);
1221 } else {
1222 LOG_ERR("no valid %s found", "hawkbit/server_addr");
1223 return false;
1224 }
1225 }
1226
1227 if (HAWKBIT_PORT_INT == 0) {
1228 if (CONFIG_HAWKBIT_PORT > 0) {
1229 hawkbit_set_server_port(CONFIG_HAWKBIT_PORT);
1230 } else {
1231 LOG_ERR("no valid %s found", "hawkbit/server_port");
1232 return false;
1233 }
1234 }
1235
1236 #ifndef CONFIG_HAWKBIT_DDI_NO_SECURITY
1237 if (strlen(HAWKBIT_DDI_SECURITY_TOKEN) == 0) {
1238 if (sizeof(CONFIG_HAWKBIT_DDI_SECURITY_TOKEN) > 1) {
1239 hawkbit_set_ddi_security_token(CONFIG_HAWKBIT_DDI_SECURITY_TOKEN);
1240 } else {
1241 LOG_ERR("no valid %s found", "hawkbit/ddi_token");
1242 return false;
1243 }
1244 }
1245 #endif /* CONFIG_HAWKBIT_DDI_NO_SECURITY */
1246
1247 return true;
1248 }
1249
s_start(void * o)1250 static void s_start(void *o)
1251 {
1252 struct s_object *s = (struct s_object *)o;
1253
1254 if (!hawkbit_initialized) {
1255 smf_set_terminate(SMF_CTX(s), HAWKBIT_NOT_INITIALIZED);
1256 return;
1257 }
1258
1259 if (!check_hawkbit_server()) {
1260 smf_set_terminate(SMF_CTX(s), HAWKBIT_NETWORKING_ERROR);
1261 return;
1262 }
1263
1264 if (k_sem_take(&probe_sem, K_NO_WAIT) != 0) {
1265 LOG_INF("hawkBit is already running");
1266 smf_set_terminate(SMF_CTX(s), HAWKBIT_PROBE_IN_PROGRESS);
1267 return;
1268 }
1269
1270 if (!boot_is_img_confirmed()) {
1271 LOG_ERR("Current image is not confirmed");
1272 k_sem_give(&probe_sem);
1273 smf_set_terminate(SMF_CTX(s), HAWKBIT_UNCONFIRMED_IMAGE);
1274 return;
1275 }
1276
1277 if (!hawkbit_get_device_identity(s->device_id, DEVICE_ID_HEX_MAX_SIZE)) {
1278 k_sem_give(&probe_sem);
1279 smf_set_terminate(SMF_CTX(s), HAWKBIT_METADATA_ERROR);
1280 return;
1281 }
1282 }
1283
s_end(void * o)1284 static void s_end(void *o)
1285 {
1286 ARG_UNUSED(o);
1287 k_sem_give(&probe_sem);
1288 }
1289
s_http_start(void * o)1290 static void s_http_start(void *o)
1291 {
1292 struct s_object *s = (struct s_object *)o;
1293
1294 if (!start_http_client(&s->hb_context.sock)) {
1295 s->hb_context.code_status = HAWKBIT_NETWORKING_ERROR;
1296 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1297 }
1298
1299 s->hb_context.response_data_size = RESPONSE_BUFFER_SIZE;
1300
1301 s->hb_context.response_data = k_calloc(s->hb_context.response_data_size, sizeof(uint8_t));
1302 if (s->hb_context.response_data == NULL) {
1303 cleanup_connection(&s->hb_context.sock);
1304 s->hb_context.code_status = HAWKBIT_ALLOC_ERROR;
1305 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1306 }
1307 }
1308
s_http_end(void * o)1309 static void s_http_end(void *o)
1310 {
1311 struct s_object *s = (struct s_object *)o;
1312
1313 cleanup_connection(&s->hb_context.sock);
1314 k_free(s->hb_context.response_data);
1315 }
1316
1317 /*
1318 * Root resource for an individual Target
1319 * GET: /{tenant}/controller/v1/{controllerId}
1320 */
s_probe(void * o)1321 static enum smf_state_result s_probe(void *o)
1322 {
1323 struct s_object *s = (struct s_object *)o;
1324 char url_buffer[URL_BUFFER_SIZE] = {0};
1325
1326 LOG_INF("Polling target data from hawkBit");
1327
1328 snprintk(url_buffer, sizeof(url_buffer), "%s/%s", HAWKBIT_JSON_URL, s->device_id);
1329
1330 if (!send_request(&s->hb_context, HAWKBIT_PROBE, url_buffer, NULL)) {
1331 LOG_ERR("Send request failed (%s)", "HAWKBIT_PROBE");
1332 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1333 return SMF_EVENT_HANDLED;
1334 }
1335
1336 if (s->hb_context.results.base.config.polling.sleep) {
1337 /* Update the sleep time. */
1338 LOG_DBG("config.polling.sleep=%s", s->hb_context.results.base.config.polling.sleep);
1339 hawkbit_update_sleep(&s->hb_context.results.base);
1340 }
1341
1342 if (s->hb_context.results.base._links.cancelAction.href) {
1343 LOG_DBG("_links.%s.href=%s", "cancelAction",
1344 s->hb_context.results.base._links.cancelAction.href);
1345 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_CANCEL]);
1346 } else if (s->hb_context.results.base._links.configData.href) {
1347 LOG_DBG("_links.%s.href=%s", "configData",
1348 s->hb_context.results.base._links.configData.href);
1349 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_CONFIG_DEVICE]);
1350 } else if (s->hb_context.results.base._links.deploymentBase.href) {
1351 LOG_DBG("_links.%s.href=%s", "deploymentBase",
1352 s->hb_context.results.base._links.deploymentBase.href);
1353 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_PROBE_DEPLOYMENT_BASE]);
1354 } else {
1355 s->hb_context.code_status = HAWKBIT_NO_UPDATE;
1356 hawkbit_event_raise(HAWKBIT_EVENT_NO_UPDATE);
1357 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1358 }
1359 return SMF_EVENT_PROPAGATE;
1360 }
1361
1362 /*
1363 * Feedback channel for cancel actions
1364 * POST: /{tenant}/controller/v1/{controllerId}/cancelAction/{actionId}/feedback
1365 */
s_cancel(void * o)1366 static enum smf_state_result s_cancel(void *o)
1367 {
1368 int ret = 0;
1369 int32_t cancel_action_id = 0;
1370 struct s_object *s = (struct s_object *)o;
1371 char *cancel_base;
1372 uint8_t status_buffer[CONFIG_HAWKBIT_STATUS_BUFFER_SIZE] = {0};
1373 char url_buffer[URL_BUFFER_SIZE] = {0};
1374 struct hawkbit_cancel cancel = {0};
1375
1376 cancel_base = hawkbit_get_url(s->hb_context.results.base._links.cancelAction.href);
1377 if (cancel_base == NULL) {
1378 LOG_ERR("Can't find %s url", "cancelAction");
1379 s->hb_context.code_status = HAWKBIT_METADATA_ERROR;
1380 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1381 return SMF_EVENT_HANDLED;
1382 }
1383
1384 snprintk(url_buffer, sizeof(url_buffer), "%s/%s", cancel_base, "feedback");
1385
1386 ret = hawkbit_find_cancel_action_id(&s->hb_context.results.base, &cancel_action_id);
1387 if (ret < 0) {
1388 LOG_ERR("Can't find %s id: %d", "cancelAction", ret);
1389 s->hb_context.code_status = HAWKBIT_METADATA_ERROR;
1390 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1391 return SMF_EVENT_HANDLED;
1392 }
1393
1394 cancel.status.execution = hawkbit_status_execution(HAWKBIT_STATUS_EXEC_CLOSED);
1395 cancel.status.result.finished = hawkbit_status_finished(
1396 hb_cfg.action_id == cancel_action_id ? HAWKBIT_STATUS_FINISHED_FAILURE
1397 : HAWKBIT_STATUS_FINISHED_SUCCESS);
1398
1399 ret = json_obj_encode_buf(json_cancel_descr, ARRAY_SIZE(json_cancel_descr), &cancel,
1400 status_buffer, sizeof(status_buffer));
1401 if (ret) {
1402 LOG_ERR("Can't encode the JSON script (%s): %d", "HAWKBIT_CANCEL", ret);
1403 s->hb_context.code_status = HAWKBIT_METADATA_ERROR;
1404 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1405 return SMF_EVENT_HANDLED;
1406 }
1407
1408 if (!send_request(&s->hb_context, HAWKBIT_CANCEL, url_buffer, status_buffer)) {
1409 LOG_ERR("Send request failed (%s)", "HAWKBIT_CANCEL");
1410 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1411 return SMF_EVENT_HANDLED;
1412 }
1413
1414 LOG_INF("From hawkBit server requested update cancellation %s",
1415 hb_cfg.action_id == cancel_action_id ? "rejected" : "accepted");
1416
1417 if (hb_cfg.action_id != cancel_action_id) {
1418 hawkbit_event_raise(HAWKBIT_EVENT_CANCEL_UPDATE);
1419 }
1420
1421 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_PROBE]);
1422 return SMF_EVENT_HANDLED;
1423 }
1424
1425 /*
1426 * Feedback channel for the config data action
1427 * PUT: /{tenant}/controller/v1/{controllerId}/configData
1428 */
s_config_device(void * o)1429 static enum smf_state_result s_config_device(void *o)
1430 {
1431 int ret = 0;
1432 struct s_object *s = (struct s_object *)o;
1433 uint8_t status_buffer[CONFIG_HAWKBIT_STATUS_BUFFER_SIZE] = {0};
1434 char *url_buffer;
1435
1436 url_buffer = hawkbit_get_url(s->hb_context.results.base._links.configData.href);
1437 if (url_buffer == NULL) {
1438 LOG_ERR("Can't find %s url", "configData");
1439 s->hb_context.code_status = HAWKBIT_METADATA_ERROR;
1440 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1441 return SMF_EVENT_HANDLED;
1442 }
1443
1444 ret = hawkbit_config_device_data_cb_handler(s->device_id, status_buffer,
1445 sizeof(status_buffer));
1446 if (ret) {
1447 LOG_ERR("Can't encode the JSON script (%s): %d", "HAWKBIT_CONFIG_DEVICE", ret);
1448 s->hb_context.code_status = HAWKBIT_METADATA_ERROR;
1449 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1450 return SMF_EVENT_HANDLED;
1451 }
1452
1453 if (!send_request(&s->hb_context, HAWKBIT_CONFIG_DEVICE, url_buffer, status_buffer)) {
1454 LOG_ERR("Send request failed (%s)", "HAWKBIT_CONFIG_DEVICE");
1455 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1456 return SMF_EVENT_HANDLED;
1457 }
1458
1459 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_PROBE]);
1460 return SMF_EVENT_HANDLED;
1461 }
1462
1463 /*
1464 * Resource for software module (Deployment Base)
1465 * GET: /{tenant}/controller/v1/{controllerId}/deploymentBase/{actionId}
1466 */
s_probe_deployment_base(void * o)1467 static enum smf_state_result s_probe_deployment_base(void *o)
1468 {
1469 int ret = 0;
1470 struct s_object *s = (struct s_object *)o;
1471 char *url_buffer;
1472
1473 url_buffer = hawkbit_get_url(s->hb_context.results.base._links.deploymentBase.href);
1474 if (url_buffer == NULL) {
1475 LOG_ERR("Can't find %s url", "deploymentBase");
1476 s->hb_context.code_status = HAWKBIT_METADATA_ERROR;
1477 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1478 return SMF_EVENT_HANDLED;
1479 }
1480
1481 if (!send_request(&s->hb_context, HAWKBIT_PROBE_DEPLOYMENT_BASE, url_buffer, NULL)) {
1482 LOG_ERR("Send request failed (%s)", "HAWKBIT_PROBE_DEPLOYMENT_BASE");
1483 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1484 return SMF_EVENT_HANDLED;
1485 }
1486
1487 hawkbit_dump_deployment(&s->hb_context.results.dep);
1488
1489 ret = hawkbit_deployment_get_action_id(&s->hb_context.results.dep,
1490 &s->hb_context.json_action_id);
1491 if (ret < 0) {
1492 s->hb_context.code_status = HAWKBIT_METADATA_ERROR;
1493 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1494 return SMF_EVENT_HANDLED;
1495 }
1496
1497 if (hb_cfg.action_id == s->hb_context.json_action_id) {
1498 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_REPORT]);
1499 return SMF_EVENT_HANDLED;
1500 }
1501
1502 LOG_INF("Ready to download update");
1503 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_DOWNLOAD]);
1504 return SMF_EVENT_HANDLED;
1505 }
1506
1507 /*
1508 * Feedback channel for the DeploymentBase action
1509 * POST: /{tenant}/controller/v1/{controllerId}/deploymentBase/{actionId}/feedback
1510 */
s_report(void * o)1511 static enum smf_state_result s_report(void *o)
1512 {
1513 int ret = 0;
1514 struct s_object *s = (struct s_object *)o;
1515 struct hawkbit_dep_fbk feedback = {
1516 .status.execution = hawkbit_status_execution(HAWKBIT_STATUS_EXEC_CLOSED),
1517 .status.result.finished = hawkbit_status_finished(HAWKBIT_STATUS_FINISHED_SUCCESS),
1518 };
1519 uint8_t status_buffer[CONFIG_HAWKBIT_STATUS_BUFFER_SIZE] = {0};
1520 char url_buffer[URL_BUFFER_SIZE] = {0};
1521
1522 snprintk(url_buffer, sizeof(url_buffer), "%s/%s/%s/%d/%s", HAWKBIT_JSON_URL,
1523 s->device_id, "deploymentBase", s->hb_context.json_action_id, "feedback");
1524
1525 LOG_INF("Reporting deployment feedback %s (%s) for action %d",
1526 feedback.status.result.finished, feedback.status.execution,
1527 s->hb_context.json_action_id);
1528
1529 ret = json_obj_encode_buf(json_dep_fbk_descr, ARRAY_SIZE(json_dep_fbk_descr), &feedback,
1530 status_buffer, sizeof(status_buffer));
1531 if (ret) {
1532 LOG_ERR("Can't encode the JSON script (%s): %d", "HAWKBIT_REPORT", ret);
1533 s->hb_context.code_status = HAWKBIT_METADATA_ERROR;
1534 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1535 return SMF_EVENT_HANDLED;
1536 }
1537
1538 if (!send_request(&s->hb_context, HAWKBIT_REPORT, url_buffer, status_buffer)) {
1539 LOG_ERR("Send request failed (%s)", "HAWKBIT_REPORT");
1540 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1541 return SMF_EVENT_HANDLED;
1542 }
1543
1544 /* After reporting the successful update to the hawkBit server, we can reset the saved
1545 * action ID to 0, as we don't need it anymore.
1546 */
1547 (void)hawkbit_device_acid_update(0);
1548
1549 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_PROBE]);
1550 return SMF_EVENT_HANDLED;
1551 }
1552
s_download_start(void * o)1553 static void s_download_start(void *o)
1554 {
1555 hawkbit_event_raise(HAWKBIT_EVENT_START_DOWNLOAD);
1556 }
1557
s_download_end(void * o)1558 static void s_download_end(void *o)
1559 {
1560 hawkbit_event_raise(HAWKBIT_EVENT_END_DOWNLOAD);
1561 }
1562
1563 /*
1564 * Resource for software module (Deployment Base)
1565 * GET: /{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/
1566 * artifacts/{fileName}
1567 */
s_download(void * o)1568 static enum smf_state_result s_download(void *o)
1569 {
1570 int ret = 0;
1571 struct s_object *s = (struct s_object *)o;
1572 struct flash_img_check fic = {0};
1573 const struct flash_area *flash_area_ptr;
1574 char *url_buffer;
1575
1576 ret = hawkbit_parse_deployment(&s->hb_context, &s->hb_context.results.dep, &url_buffer);
1577 if (ret < 0) {
1578 LOG_ERR("Failed to parse %s: %d", "deploymentBase", ret);
1579 s->hb_context.code_status = HAWKBIT_METADATA_ERROR;
1580 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1581 return SMF_EVENT_HANDLED;
1582 }
1583
1584 flash_img_init(&s->hb_context.flash_ctx);
1585
1586 /* The flash_area pointer has to be copied before the download starts
1587 * because the flash_area will be set to NULL after the download has finished.
1588 */
1589 flash_area_ptr = s->hb_context.flash_ctx.flash_area;
1590
1591 #ifdef CONFIG_HAWKBIT_SAVE_PROGRESS
1592 stream_flash_progress_load(&s->hb_context.flash_ctx.stream, "hawkbit/flash_progress");
1593 #endif
1594
1595 if (!send_request(&s->hb_context, HAWKBIT_DOWNLOAD, url_buffer, NULL)) {
1596 LOG_ERR("Send request failed (%s)", "HAWKBIT_DOWNLOAD");
1597 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1598 return SMF_EVENT_HANDLED;
1599 }
1600
1601 /* Check if download finished */
1602 if (!s->hb_context.final_data_received) {
1603 LOG_ERR("Download incomplete");
1604 s->hb_context.code_status = HAWKBIT_DOWNLOAD_ERROR;
1605 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1606 return SMF_EVENT_HANDLED;
1607 }
1608
1609 #ifdef CONFIG_HAWKBIT_SAVE_PROGRESS
1610 stream_flash_progress_clear(&s->hb_context.flash_ctx.stream, "hawkbit/flash_progress");
1611 #endif
1612
1613 /* Verify the hash of the stored firmware */
1614 fic.match = s->hb_context.dl.file_hash;
1615 fic.clen = s->hb_context.dl.downloaded_size;
1616 if (flash_img_check(&s->hb_context.flash_ctx, &fic, flash_area_ptr->fa_id)) {
1617 LOG_ERR("Failed to validate stored firmware");
1618 s->hb_context.code_status = HAWKBIT_DOWNLOAD_ERROR;
1619 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1620 return SMF_EVENT_HANDLED;
1621 }
1622
1623 /* Request mcuboot to upgrade */
1624 if (boot_set_next(flash_area_ptr, false, false)) {
1625 LOG_ERR("Failed to mark the image in slot 1 as pending");
1626 s->hb_context.code_status = HAWKBIT_DOWNLOAD_ERROR;
1627 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1628 return SMF_EVENT_HANDLED;
1629 }
1630
1631 /* If everything is successful */
1632 s->hb_context.code_status = HAWKBIT_UPDATE_INSTALLED;
1633 hawkbit_device_acid_update(s->hb_context.json_action_id);
1634 hawkbit_event_raise(HAWKBIT_EVENT_UPDATE_DOWNLOADED);
1635
1636 smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
1637 return SMF_EVENT_HANDLED;
1638 }
1639
s_terminate(void * o)1640 static void s_terminate(void *o)
1641 {
1642 struct s_object *s = (struct s_object *)o;
1643
1644 #ifdef CONFIG_HAWKBIT_EVENT_CALLBACKS
1645 if (IN_RANGE(s->hb_context.code_status, HAWKBIT_NETWORKING_ERROR,
1646 HAWKBIT_PROBE_IN_PROGRESS)) {
1647 hawkbit_event_raise(HAWKBIT_EVENT_ERROR);
1648 switch (s->hb_context.code_status) {
1649 case HAWKBIT_NETWORKING_ERROR:
1650 hawkbit_event_raise(HAWKBIT_EVENT_ERROR_NETWORKING);
1651 break;
1652
1653 case HAWKBIT_PERMISSION_ERROR:
1654 hawkbit_event_raise(HAWKBIT_EVENT_ERROR_PERMISSION);
1655 break;
1656
1657 case HAWKBIT_METADATA_ERROR:
1658 hawkbit_event_raise(HAWKBIT_EVENT_ERROR_METADATA);
1659 break;
1660
1661 case HAWKBIT_DOWNLOAD_ERROR:
1662 hawkbit_event_raise(HAWKBIT_EVENT_ERROR_DOWNLOAD);
1663 break;
1664
1665 case HAWKBIT_ALLOC_ERROR:
1666 hawkbit_event_raise(HAWKBIT_EVENT_ERROR_ALLOC);
1667 break;
1668 default:
1669 break;
1670 }
1671 }
1672 #endif
1673
1674 smf_set_terminate(SMF_CTX(s), s->hb_context.code_status);
1675 }
1676
1677 /* clang-format off */
1678 static const struct smf_state hawkbit_states[] = {
1679 [S_HAWKBIT_START] = SMF_CREATE_STATE(
1680 s_start,
1681 NULL,
1682 s_end,
1683 NULL,
1684 NULL),
1685 [S_HAWKBIT_HTTP] = SMF_CREATE_STATE(
1686 s_http_start,
1687 NULL,
1688 s_http_end,
1689 &hawkbit_states[S_HAWKBIT_START],
1690 NULL),
1691 [S_HAWKBIT_PROBE] = SMF_CREATE_STATE(
1692 NULL,
1693 s_probe,
1694 NULL,
1695 &hawkbit_states[S_HAWKBIT_HTTP],
1696 NULL),
1697 [S_HAWKBIT_CONFIG_DEVICE] = SMF_CREATE_STATE(
1698 NULL,
1699 s_config_device,
1700 NULL,
1701 &hawkbit_states[S_HAWKBIT_HTTP],
1702 NULL),
1703 [S_HAWKBIT_CANCEL] = SMF_CREATE_STATE(
1704 NULL,
1705 s_cancel,
1706 NULL,
1707 &hawkbit_states[S_HAWKBIT_HTTP],
1708 NULL),
1709 [S_HAWKBIT_PROBE_DEPLOYMENT_BASE] = SMF_CREATE_STATE(
1710 NULL,
1711 s_probe_deployment_base,
1712 NULL,
1713 &hawkbit_states[S_HAWKBIT_HTTP],
1714 NULL),
1715 [S_HAWKBIT_REPORT] = SMF_CREATE_STATE(
1716 NULL,
1717 s_report,
1718 NULL,
1719 &hawkbit_states[S_HAWKBIT_HTTP],
1720 NULL),
1721 [S_HAWKBIT_DOWNLOAD] = SMF_CREATE_STATE(
1722 s_download_start,
1723 s_download,
1724 s_download_end,
1725 &hawkbit_states[S_HAWKBIT_HTTP],
1726 NULL),
1727 [S_HAWKBIT_TERMINATE] = SMF_CREATE_STATE(
1728 s_terminate,
1729 NULL,
1730 NULL,
1731 NULL,
1732 NULL),
1733 };
1734 /* clang-format on */
hawkbit_probe(void)1735 enum hawkbit_response hawkbit_probe(void)
1736 {
1737 int32_t ret = 0;
1738 struct s_object s_obj = {0};
1739
1740 smf_set_initial(SMF_CTX(&s_obj), &hawkbit_states[S_HAWKBIT_PROBE]);
1741 hawkbit_event_raise(HAWKBIT_EVENT_START_RUN);
1742
1743 while (1) {
1744 ret = smf_run_state(SMF_CTX(&s_obj));
1745 if (ret != 0) {
1746 hawkbit_event_raise(HAWKBIT_EVENT_END_RUN);
1747 return (enum hawkbit_response)ret;
1748 }
1749 }
1750 }
1751