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