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