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