Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) The ThingSet Project Contributors
3 : : *
4 : : * SPDX-License-Identifier: Apache-2.0
5 : : */
6 : :
7 : : #include <thingset.h>
8 : :
9 : : #include "thingset_internal.h"
10 : :
11 : : #include <zephyr/logging/log.h>
12 : : #include <zephyr/toolchain/common.h>
13 : :
14 : : #include <stdio.h>
15 : : #include <stdlib.h>
16 : :
17 : : LOG_MODULE_REGISTER(thingset, CONFIG_THINGSET_LOG_LEVEL);
18 : :
19 : : /* pointers to iterable section data */
20 : : #ifdef STRUCT_SECTION_START_EXTERN
21 : : STRUCT_SECTION_START_EXTERN(thingset_data_object);
22 : : STRUCT_SECTION_END_EXTERN(thingset_data_object);
23 : : #else
24 : : /* Zephyr versions below v3.4 are missing some macros... */
25 : : extern struct thingset_data_object _thingset_data_object_list_start[];
26 : : extern struct thingset_data_object _thingset_data_object_list_end[];
27 : : #define TYPE_SECTION_START(secname) _CONCAT(_##secname, _list_start)
28 : : #endif /* STRUCT_SECTION_START_EXTERN */
29 : :
30 : : /* dummy objects to avoid using NULL pointer for root object or _Paths/_Metadata overlays */
31 : : static struct thingset_data_object root_object = THINGSET_GROUP(0, 0, "", NULL);
32 : : static struct thingset_data_object paths_object =
33 : : THINGSET_GROUP(0, THINGSET_ID_PATHS, "_Paths", NULL);
34 : : #ifdef CONFIG_THINGSET_METADATA_ENDPOINT
35 : : static struct thingset_data_object metadata_object =
36 : : THINGSET_GROUP(0, THINGSET_ID_METADATA, "_Metadata", NULL);
37 : : #endif
38 : :
39 : : static char *type_name_lookup[THINGSET_TYPE_FN_I32 + 1] = {
40 : : "bool", "u8", "i8", "u16", "i16", "u32", "i32",
41 : : "u64", "i64", "f32", "decimal", "string", "buffer", "array",
42 : : "record", "group", "subset", "()->()", "()->(i32)"
43 : : };
44 : :
45 : 3 : static void check_id_duplicates(const struct thingset_data_object *objects, size_t num)
46 : : {
47 [ + + ]: 82 : for (unsigned int i = 0; i < num; i++) {
48 [ + + ]: 2576 : for (unsigned int j = i + 1; j < num; j++) {
49 [ - + ]: 2497 : if (objects[i].id == objects[j].id) {
50 [ # # # # ]: 0 : LOG_ERR("Duplicate data object ID 0x%X.\n", objects[i].id);
51 : : }
52 : : }
53 : : }
54 : 3 : }
55 : :
56 : 7 : static void thingset_init_common(struct thingset_context *ts)
57 : : {
58 : : #ifdef CONFIG_THINGSET_OBJECT_LOOKUP_MAP
59 : : for (unsigned int b = 0; b < CONFIG_THINGSET_OBJECT_LOOKUP_BUCKETS; b++) {
60 : : sys_slist_init(&ts->data_objects_lookup[b]);
61 : : }
62 : :
63 : : for (unsigned int i = 0; i < ts->num_objects; i++) {
64 : : struct thingset_data_object *object = &ts->data_objects[i];
65 : : sys_slist_append(
66 : : &ts->data_objects_lookup[object->id % CONFIG_THINGSET_OBJECT_LOOKUP_BUCKETS],
67 : : &object->node);
68 : : }
69 : : #endif
70 : 7 : ts->auth_flags = THINGSET_USR_MASK;
71 : :
72 : 7 : k_sem_init(&ts->lock, 1, 1);
73 : 7 : }
74 : :
75 : 3 : void thingset_init(struct thingset_context *ts, struct thingset_data_object *objects,
76 : : size_t num_objects)
77 : : {
78 : 3 : check_id_duplicates(objects, num_objects);
79 : :
80 : 3 : ts->data_objects = objects;
81 : 3 : ts->num_objects = num_objects;
82 : 3 : thingset_init_common(ts);
83 : 3 : }
84 : :
85 : 4 : void thingset_init_global(struct thingset_context *ts)
86 : : {
87 : : /* duplicates are checked at compile-time */
88 : :
89 : 4 : ts->data_objects = TYPE_SECTION_START(thingset_data_object);
90 : 4 : STRUCT_SECTION_COUNT(thingset_data_object, &ts->num_objects);
91 : 4 : thingset_init_common(ts);
92 : 4 : }
93 : :
94 : 129 : int thingset_process_message(struct thingset_context *ts, const uint8_t *msg, size_t msg_len,
95 : : uint8_t *rsp, size_t rsp_size)
96 : : {
97 : : int ret;
98 : :
99 [ + - - + ]: 129 : if (msg == NULL || msg_len < 1) {
100 : 0 : return -THINGSET_ERR_BAD_REQUEST;
101 : : }
102 : :
103 [ + - - + ]: 129 : if (rsp == NULL || rsp_size < 4) {
104 : : /* response buffer with at least 4 bytes required to fit minimum response */
105 : 0 : return -THINGSET_ERR_INTERNAL_SERVER_ERR;
106 : : }
107 : :
108 [ - + ]: 129 : if (k_sem_take(&ts->lock, K_MSEC(THINGSET_CONTEXT_LOCK_TIMEOUT_MS)) != 0) {
109 [ # # # # ]: 0 : LOG_ERR("ThingSet context lock timed out");
110 : 0 : return -THINGSET_ERR_INTERNAL_SERVER_ERR;
111 : : }
112 : :
113 : 129 : ts->msg = msg;
114 : 129 : ts->msg_len = msg_len;
115 : 129 : ts->msg_pos = 0;
116 : :
117 : 129 : ts->rsp = rsp;
118 : 129 : ts->rsp_size = rsp_size;
119 : 129 : ts->rsp_pos = 0;
120 : :
121 [ + + ]: 129 : if (IS_ENABLED(CONFIG_THINGSET_TEXT_MODE) && ts->msg[0] >= 0x20) {
122 : 65 : ret = thingset_txt_process(ts);
123 : : }
124 : : else {
125 : 64 : ret = thingset_bin_process(ts);
126 : : }
127 : :
128 : 129 : k_sem_give(&ts->lock);
129 : :
130 : 129 : return ret;
131 : : }
132 : :
133 : 3 : int thingset_export_subsets_progressively(struct thingset_context *ts, uint8_t *buf,
134 : : size_t buf_size, uint16_t subsets,
135 : : enum thingset_data_format format, unsigned int *index,
136 : : size_t *len)
137 : : {
138 [ + + ]: 3 : if (*index == 0) {
139 [ - + ]: 2 : if (k_sem_take(&ts->lock, K_MSEC(THINGSET_CONTEXT_LOCK_TIMEOUT_MS)) != 0) {
140 [ # # # # ]: 0 : LOG_ERR("ThingSet context lock timed out");
141 : 0 : return -THINGSET_ERR_INTERNAL_SERVER_ERR;
142 : : }
143 : :
144 : 2 : ts->rsp = buf;
145 : 2 : ts->rsp_size = buf_size;
146 : 2 : ts->rsp_pos = 0;
147 : :
148 [ + - ]: 2 : switch (format) {
149 : 2 : case THINGSET_BIN_IDS_VALUES:
150 : 2 : ts->endpoint.use_ids = true;
151 : 2 : thingset_bin_setup(ts, 0);
152 : 2 : break;
153 : 0 : default:
154 : 0 : k_sem_give(&ts->lock);
155 : 0 : return -THINGSET_ERR_NOT_IMPLEMENTED;
156 : : }
157 : : }
158 : 3 : int ret = thingset_bin_export_subsets_progressively(ts, subsets, index, len);
159 [ + + ]: 3 : if (ret <= 0) {
160 : 2 : k_sem_give(&ts->lock);
161 : : }
162 : :
163 : 3 : return ret;
164 : : }
165 : :
166 : 2 : int thingset_export_subsets(struct thingset_context *ts, uint8_t *buf, size_t buf_size,
167 : : uint16_t subsets, enum thingset_data_format format)
168 : : {
169 : : int ret;
170 : :
171 [ - + ]: 2 : if (k_sem_take(&ts->lock, K_MSEC(THINGSET_CONTEXT_LOCK_TIMEOUT_MS)) != 0) {
172 [ # # # # ]: 0 : LOG_ERR("ThingSet context lock timed out");
173 : 0 : return -THINGSET_ERR_INTERNAL_SERVER_ERR;
174 : : }
175 : :
176 : 2 : ts->rsp = buf;
177 : 2 : ts->rsp_size = buf_size;
178 : 2 : ts->rsp_pos = 0;
179 : :
180 [ + + - ]: 2 : switch (format) {
181 : : #ifdef CONFIG_THINGSET_TEXT_MODE
182 : 1 : case THINGSET_TXT_NAMES_VALUES:
183 : 1 : thingset_txt_setup(ts);
184 : 1 : break;
185 : : #endif
186 : 1 : case THINGSET_BIN_IDS_VALUES:
187 : 1 : ts->endpoint.use_ids = true;
188 : 1 : thingset_bin_setup(ts, 0);
189 : 1 : break;
190 : 0 : default:
191 : 0 : return -THINGSET_ERR_NOT_IMPLEMENTED;
192 : : }
193 : :
194 : 2 : ret = ts->api->serialize_subsets(ts, subsets);
195 : :
196 : 2 : ts->api->serialize_finish(ts);
197 : :
198 [ + - ]: 2 : if (ret == 0) {
199 : 2 : ret = ts->rsp_pos;
200 : : }
201 : :
202 : 2 : k_sem_give(&ts->lock);
203 : :
204 : 2 : return ret;
205 : : }
206 : :
207 : 2 : int thingset_export_item(struct thingset_context *ts, uint8_t *buf, size_t buf_size,
208 : : const struct thingset_data_object *obj, enum thingset_data_format format)
209 : : {
210 : : int ret;
211 : :
212 [ - + ]: 2 : if (k_sem_take(&ts->lock, K_MSEC(THINGSET_CONTEXT_LOCK_TIMEOUT_MS)) != 0) {
213 [ # # # # ]: 0 : LOG_ERR("ThingSet context lock timed out");
214 : 0 : return -THINGSET_ERR_INTERNAL_SERVER_ERR;
215 : : }
216 : :
217 : 2 : ts->rsp = buf;
218 : 2 : ts->rsp_size = buf_size;
219 : 2 : ts->rsp_pos = 0;
220 : :
221 [ - + - ]: 2 : switch (format) {
222 : : #ifdef CONFIG_THINGSET_TEXT_MODE
223 : 0 : case THINGSET_TXT_VALUES_ONLY:
224 : 0 : thingset_txt_setup(ts);
225 : 0 : break;
226 : : #endif
227 : 2 : case THINGSET_BIN_VALUES_ONLY:
228 : 2 : ts->endpoint.use_ids = true;
229 : 2 : thingset_bin_setup(ts, 0);
230 : 2 : break;
231 : 0 : default:
232 : 0 : ret = -THINGSET_ERR_NOT_IMPLEMENTED;
233 : 0 : goto out;
234 : : }
235 : :
236 : 2 : ret = ts->api->serialize_value(ts, obj);
237 : :
238 : 2 : ts->api->serialize_finish(ts);
239 : :
240 [ - + ]: 2 : if (ret == 0) {
241 : 2 : ret = ts->rsp_pos;
242 : : }
243 : :
244 : 0 : out:
245 : 2 : k_sem_give(&ts->lock);
246 : :
247 : 2 : return ret;
248 : : }
249 : :
250 : 6 : struct thingset_data_object *thingset_iterate_subsets(struct thingset_context *ts, uint16_t subset,
251 : : struct thingset_data_object *start_obj)
252 : : {
253 [ + + ]: 6 : if (start_obj == NULL) {
254 : 1 : start_obj = ts->data_objects;
255 : : }
256 : :
257 : 6 : struct thingset_data_object *end_obj = ts->data_objects + ts->num_objects;
258 [ + + ]: 72 : for (struct thingset_data_object *obj = start_obj; obj < end_obj; obj++) {
259 [ + + ]: 71 : if (obj->subsets & subset) {
260 : 5 : return obj;
261 : : }
262 : : }
263 : :
264 : 1 : return NULL;
265 : : }
266 : :
267 : 0 : int thingset_import_data_progressively(struct thingset_context *ts, const uint8_t *data, size_t len,
268 : : enum thingset_data_format format, uint8_t auth_flags,
269 : : uint32_t *last_id, size_t *consumed)
270 : : {
271 : 0 : int err = 0;
272 : :
273 [ # # ]: 0 : if (*last_id == 0) {
274 [ # # ]: 0 : if (k_sem_take(&ts->lock, K_MSEC(THINGSET_CONTEXT_LOCK_TIMEOUT_MS)) != 0) {
275 [ # # # # ]: 0 : LOG_ERR("ThingSet context lock timed out");
276 : 0 : return -THINGSET_ERR_INTERNAL_SERVER_ERR;
277 : : }
278 : :
279 : 0 : ts->msg = data;
280 : 0 : ts->msg_len = len;
281 : 0 : ts->msg_pos = 0;
282 : 0 : ts->rsp = NULL;
283 : 0 : ts->rsp_size = 0;
284 : 0 : ts->rsp_pos = 0;
285 : :
286 [ # # ]: 0 : switch (format) {
287 : 0 : case THINGSET_BIN_IDS_VALUES:
288 : 0 : ts->endpoint.use_ids = true;
289 : 0 : thingset_bin_setup(ts, 0);
290 : 0 : ts->msg_payload = data;
291 : 0 : ts->api->deserialize_payload_reset(ts);
292 : 0 : break;
293 : 0 : default:
294 : 0 : err = -THINGSET_ERR_NOT_IMPLEMENTED;
295 : 0 : k_sem_give(&ts->lock);
296 : 0 : break;
297 : : }
298 : :
299 [ # # ]: 0 : if (err) {
300 : 0 : return err;
301 : : }
302 : : }
303 : :
304 : 0 : err = thingset_bin_import_data_progressively(ts, auth_flags, len, last_id, consumed);
305 [ # # ]: 0 : if (err < 0) {
306 : 0 : k_sem_give(&ts->lock);
307 : : }
308 : 0 : return err;
309 : : }
310 : :
311 : 0 : int thingset_import_data_progressively_end(struct thingset_context *ts)
312 : : {
313 : 0 : k_sem_give(&ts->lock);
314 : 0 : return 0;
315 : : }
316 : :
317 : 1 : int thingset_import_data(struct thingset_context *ts, const uint8_t *data, size_t len,
318 : : uint8_t auth_flags, enum thingset_data_format format)
319 : : {
320 : : int err;
321 : :
322 [ - + ]: 1 : if (k_sem_take(&ts->lock, K_MSEC(THINGSET_CONTEXT_LOCK_TIMEOUT_MS)) != 0) {
323 [ # # # # ]: 0 : LOG_ERR("ThingSet context lock timed out");
324 : 0 : return -THINGSET_ERR_INTERNAL_SERVER_ERR;
325 : : }
326 : :
327 : 1 : ts->msg = data;
328 : 1 : ts->msg_len = len;
329 : 1 : ts->msg_pos = 0;
330 : 1 : ts->rsp = NULL;
331 : 1 : ts->rsp_size = 0;
332 : 1 : ts->rsp_pos = 0;
333 : :
334 [ + - ]: 1 : switch (format) {
335 : 1 : case THINGSET_BIN_IDS_VALUES:
336 : 1 : ts->endpoint.use_ids = true;
337 : 1 : thingset_bin_setup(ts, 0);
338 : 1 : ts->msg_payload = data;
339 : 1 : ts->api->deserialize_payload_reset(ts);
340 : 1 : err = thingset_bin_import_data(ts, auth_flags, format);
341 : 1 : break;
342 : 0 : default:
343 : 0 : err = -THINGSET_ERR_NOT_IMPLEMENTED;
344 : 0 : break;
345 : : }
346 : :
347 : 1 : k_sem_give(&ts->lock);
348 : :
349 : 1 : return err;
350 : : }
351 : :
352 : 3 : int thingset_import_report(struct thingset_context *ts, const uint8_t *data, size_t len,
353 : : uint8_t auth_flags, enum thingset_data_format format, uint16_t subset)
354 : : {
355 : : int err;
356 : :
357 [ - + ]: 3 : if (k_sem_take(&ts->lock, K_MSEC(THINGSET_CONTEXT_LOCK_TIMEOUT_MS)) != 0) {
358 [ # # # # ]: 0 : LOG_ERR("ThingSet context lock timed out");
359 : 0 : return -THINGSET_ERR_INTERNAL_SERVER_ERR;
360 : : }
361 : :
362 : 3 : ts->msg = data;
363 : 3 : ts->msg_len = len;
364 : 3 : ts->msg_pos = 0;
365 : 3 : ts->rsp = NULL;
366 : 3 : ts->rsp_size = 0;
367 : 3 : ts->rsp_pos = 0;
368 : :
369 [ + - ]: 3 : switch (format) {
370 : 3 : case THINGSET_BIN_IDS_VALUES:
371 : 3 : ts->endpoint.use_ids = true;
372 : 3 : thingset_bin_setup(ts, 0);
373 : 3 : ts->decoder->elem_count = 2;
374 : 3 : ts->msg_payload = data;
375 : 3 : err = thingset_bin_import_report(ts, auth_flags, subset);
376 : 3 : break;
377 : 0 : default:
378 : 0 : err = -THINGSET_ERR_NOT_IMPLEMENTED;
379 : 0 : break;
380 : : }
381 : :
382 : 3 : k_sem_give(&ts->lock);
383 : :
384 : 3 : return err;
385 : : }
386 : :
387 : 3 : static int deserialize_value_callback(struct thingset_context *ts,
388 : : const struct thingset_data_object *item_offset)
389 : : {
390 : 3 : return ts->api->deserialize_value(ts, item_offset, false);
391 : : }
392 : :
393 : 2 : int thingset_import_record(struct thingset_context *ts, const uint8_t *data, size_t len,
394 : : struct thingset_endpoint *endpoint, enum thingset_data_format format)
395 : : {
396 : : int err;
397 : :
398 [ - + ]: 2 : if (k_sem_take(&ts->lock, K_MSEC(THINGSET_CONTEXT_LOCK_TIMEOUT_MS)) != 0) {
399 [ # # # # ]: 0 : LOG_ERR("ThingSet context lock timed out");
400 : 0 : return -THINGSET_ERR_INTERNAL_SERVER_ERR;
401 : : }
402 : :
403 : 2 : ts->msg = data;
404 : 2 : ts->msg_len = len;
405 : 2 : ts->msg_pos = 0;
406 : 2 : ts->rsp = NULL;
407 : 2 : ts->rsp_size = 0;
408 : 2 : ts->rsp_pos = 0;
409 : :
410 : 2 : ts->endpoint = *endpoint;
411 : :
412 [ + + - ]: 2 : switch (format) {
413 : : #ifdef CONFIG_THINGSET_TEXT_MODE
414 : 1 : case THINGSET_TXT_NAMES_VALUES:
415 : 1 : thingset_txt_setup(ts);
416 : 1 : ts->msg_payload = data;
417 : 1 : ts->api->deserialize_payload_reset(ts);
418 : 1 : break;
419 : : #endif
420 : 1 : case THINGSET_BIN_IDS_VALUES:
421 : 1 : ts->endpoint.use_ids = true;
422 : 1 : thingset_bin_setup(ts, 0);
423 : 1 : ts->msg_payload = data;
424 : 1 : ts->api->deserialize_payload_reset(ts);
425 : 1 : break;
426 : 0 : default:
427 : 0 : err = -THINGSET_ERR_NOT_IMPLEMENTED;
428 : 0 : goto out;
429 : : }
430 : :
431 : 2 : err = ts->api->deserialize_map_start(ts);
432 [ - + ]: 2 : if (err != 0) {
433 : 0 : goto out;
434 : : }
435 : :
436 : : const struct thingset_data_object *item;
437 [ + + ]: 5 : while ((err = ts->api->deserialize_child(ts, &item)) != -THINGSET_ERR_DESERIALIZATION_FINISHED)
438 : : {
439 [ - + ]: 3 : if (err == -THINGSET_ERR_NOT_FOUND) {
440 : : /* silently ignore non-existing record items and skip value */
441 : 0 : ts->api->deserialize_skip(ts);
442 : 0 : continue;
443 : : }
444 [ - + ]: 3 : else if (err != 0) {
445 : 0 : goto out;
446 : : }
447 : :
448 : 3 : struct thingset_records *records = ts->endpoint.object->data.records;
449 : 3 : uint8_t *record_ptr =
450 : 3 : (uint8_t *)records->records + ts->endpoint.index * records->record_size;
451 : 3 : err = thingset_common_prepare_record_element(ts, item, record_ptr,
452 : : deserialize_value_callback);
453 : :
454 [ - + ]: 3 : if (err != 0) {
455 : 0 : goto out;
456 : : }
457 : : }
458 : :
459 [ - + ]: 2 : err = err == -THINGSET_ERR_DESERIALIZATION_FINISHED ? 0 : ts->api->deserialize_finish(ts);
460 : :
461 : 2 : out:
462 : 2 : k_sem_give(&ts->lock);
463 : :
464 : 2 : return err;
465 : : }
466 : :
467 : 8 : int thingset_report_path(struct thingset_context *ts, char *buf, size_t buf_size, const char *path,
468 : : enum thingset_data_format format)
469 : : {
470 : : int err;
471 : :
472 [ - + ]: 8 : if (k_sem_take(&ts->lock, K_MSEC(THINGSET_CONTEXT_LOCK_TIMEOUT_MS)) != 0) {
473 [ # # # # ]: 0 : LOG_ERR("ThingSet context lock timed out");
474 : 0 : return -THINGSET_ERR_INTERNAL_SERVER_ERR;
475 : : }
476 : :
477 : 8 : ts->rsp = buf;
478 : 8 : ts->rsp_size = buf_size;
479 : 8 : ts->rsp_pos = 0;
480 : :
481 : 8 : err = thingset_endpoint_by_path(ts, &ts->endpoint, path, strlen(path));
482 [ - + ]: 8 : if (err != 0) {
483 : 0 : goto out;
484 : : }
485 [ - + ]: 8 : else if (ts->endpoint.object == NULL) {
486 : 0 : err = -THINGSET_ERR_BAD_REQUEST;
487 : 0 : goto out;
488 : : }
489 : :
490 [ + + + - ]: 8 : switch (format) {
491 : : #ifdef CONFIG_THINGSET_TEXT_MODE
492 : 4 : case THINGSET_TXT_NAMES_VALUES:
493 : 4 : thingset_txt_setup(ts);
494 : 4 : break;
495 : : #endif
496 : 3 : case THINGSET_BIN_IDS_VALUES:
497 : 3 : ts->endpoint.use_ids = true;
498 : 3 : thingset_bin_setup(ts, 1);
499 : 3 : break;
500 : 1 : case THINGSET_BIN_NAMES_VALUES:
501 : 1 : ts->endpoint.use_ids = false;
502 : 1 : thingset_bin_setup(ts, 1);
503 : 1 : break;
504 : 0 : default:
505 : 0 : err = -THINGSET_ERR_NOT_IMPLEMENTED;
506 : 0 : goto out;
507 : : }
508 : :
509 : 8 : err = ts->api->serialize_report_header(ts, path);
510 [ - + ]: 8 : if (err != 0) {
511 : 0 : goto out;
512 : : }
513 : :
514 [ + + - + : 8 : switch (ts->endpoint.object->type) {
- ]
515 : 2 : case THINGSET_TYPE_GROUP:
516 : 2 : err = thingset_common_serialize_group(ts, ts->endpoint.object);
517 : 2 : break;
518 : 2 : case THINGSET_TYPE_SUBSET:
519 : 2 : err = ts->api->serialize_subsets(ts, ts->endpoint.object->data.subset);
520 : 2 : break;
521 : 0 : case THINGSET_TYPE_FN_VOID:
522 : : case THINGSET_TYPE_FN_I32:
523 : : /* bad request, as we can't read exec object's values */
524 : 0 : err = -THINGSET_ERR_BAD_REQUEST;
525 : 0 : break;
526 : 4 : case THINGSET_TYPE_RECORDS:
527 [ + - ]: 4 : if (ts->endpoint.index != THINGSET_ENDPOINT_INDEX_NONE) {
528 : 4 : err = thingset_common_serialize_record(ts, ts->endpoint.object, ts->endpoint.index);
529 : 4 : break;
530 : : }
531 : : /* fallthrough */
532 : : default:
533 : 0 : err = ts->api->serialize_value(ts, ts->endpoint.object);
534 : 0 : break;
535 : : }
536 : :
537 : 8 : ts->api->serialize_finish(ts);
538 : :
539 [ - + ]: 8 : if (err == 0) {
540 : 8 : err = ts->rsp_pos;
541 : : }
542 : :
543 : 0 : out:
544 : 8 : k_sem_give(&ts->lock);
545 : :
546 : 8 : return err;
547 : : }
548 : :
549 : 3 : void thingset_set_authentication(struct thingset_context *ts, uint8_t flags)
550 : : {
551 : 3 : ts->auth_flags = flags;
552 : 3 : }
553 : :
554 : 2 : void thingset_set_update_callback(struct thingset_context *ts, const uint16_t subsets,
555 : : void (*update_cb)(void))
556 : : {
557 : 2 : ts->update_subsets = subsets;
558 : 2 : ts->update_cb = update_cb;
559 : 2 : }
560 : :
561 : 184 : struct thingset_data_object *thingset_get_child_by_name(struct thingset_context *ts,
562 : : uint16_t parent_id, const char *name,
563 : : size_t len)
564 : : {
565 [ + + ]: 6316 : for (unsigned int i = 0; i < ts->num_objects; i++) {
566 [ + + ]: 6304 : if (ts->data_objects[i].parent_id == parent_id
567 [ + + ]: 1041 : && strncmp(ts->data_objects[i].name, name, len) == 0
568 : : // without length check foo and fooBar would be recognized as equal
569 [ + + ]: 178 : && strlen(ts->data_objects[i].name) == len)
570 : : {
571 : 172 : return &(ts->data_objects[i]);
572 : : }
573 : : }
574 : :
575 : : #ifdef CONFIG_THINGSET_METADATA_ENDPOINT
576 [ + + + - ]: 12 : if (len == strlen(metadata_object.name) && strncmp(name, metadata_object.name, len) == 0) {
577 : 1 : return &metadata_object;
578 : : }
579 : : #endif
580 : :
581 : 11 : return NULL;
582 : : }
583 : :
584 : 164 : struct thingset_data_object *thingset_get_object_by_id(struct thingset_context *ts, uint16_t id)
585 : : {
586 : : #ifdef CONFIG_THINGSET_OBJECT_LOOKUP_MAP
587 : : sys_slist_t *list = &ts->data_objects_lookup[id % CONFIG_THINGSET_OBJECT_LOOKUP_BUCKETS];
588 : : sys_snode_t *pnode;
589 : : struct thingset_data_object *object;
590 : : SYS_SLIST_FOR_EACH_NODE(list, pnode)
591 : : {
592 : : object = CONTAINER_OF(pnode, struct thingset_data_object, node);
593 : : if (object->id == id) {
594 : : return object;
595 : : }
596 : : }
597 : : #else
598 [ + + ]: 6755 : for (unsigned int i = 0; i < ts->num_objects; i++) {
599 [ + + ]: 6730 : if (ts->data_objects[i].id == id) {
600 : 139 : return &(ts->data_objects[i]);
601 : : }
602 : : }
603 : : #endif /* CONFIG_THINGSET_OBJECT_LOOKUP_MAP */
604 : 25 : return NULL;
605 : : }
606 : :
607 : 101 : struct thingset_data_object *thingset_get_object_by_path(struct thingset_context *ts,
608 : : const char *path, size_t path_len,
609 : : int *index)
610 : : {
611 : 101 : *index = THINGSET_ENDPOINT_INDEX_NONE;
612 : :
613 : 101 : struct thingset_data_object *object = NULL;
614 : 101 : const char *start = path;
615 : : const char *end;
616 : 101 : uint16_t parent = 0;
617 : :
618 : : /* maximum depth of 10 assumed */
619 [ + - ]: 149 : for (int i = 0; i < 10; i++) {
620 : 149 : end = strchr(start, '/');
621 [ + + + + ]: 149 : if (end == NULL || end >= path + path_len) {
622 : : /* reached at the end of the path */
623 [ + + + + : 100 : if (object != NULL && object->type == THINGSET_TYPE_RECORDS && *start >= '0'
+ + ]
624 [ + - ]: 10 : && *start <= '9')
625 : : {
626 : : /* numeric ID to select index in an array of records */
627 : : /*
628 : : * Note: strtoul and atoi only work with null-terminated strings, so we have to use
629 : : * an own implementation to be able to parse CBOR-encoded strings.
630 : : */
631 : 10 : end = path + path_len; /* ensure we never go past the end of the declared length */
632 : 10 : *index = 0;
633 : : do {
634 [ + - + - ]: 10 : if (*start >= '0' && *start <= '9') {
635 : 10 : *index = (*index) * 10 + *start - '0';
636 : 10 : start++;
637 : : }
638 : : else {
639 : 0 : return NULL;
640 : : }
641 [ - + ]: 10 : } while (start < end);
642 : : }
643 [ + + ]: 90 : else if (*start == '-') {
644 : : /* non-existent element behind the last array element */
645 : 2 : *index = THINGSET_ENDPOINT_INDEX_NEW;
646 : : }
647 : : else {
648 : 88 : object = thingset_get_child_by_name(ts, parent, start, path + path_len - start);
649 : : }
650 : 100 : break;
651 : : }
652 [ + + ]: 49 : else if (end == path + path_len - 1) {
653 : : /* path ends with slash */
654 : 1 : object = thingset_get_child_by_name(ts, parent, start, end - start);
655 : 1 : break;
656 : : }
657 : : else {
658 : : /* go further down the path */
659 : 48 : object = thingset_get_child_by_name(ts, parent, start, end - start);
660 [ + - ]: 48 : if (object) {
661 : 48 : parent = object->id;
662 : 48 : start = end + 1;
663 : : }
664 : : else {
665 : 0 : break;
666 : : }
667 : : }
668 : : }
669 : :
670 : 101 : return object;
671 : : }
672 : :
673 : 112 : int thingset_endpoint_by_path(struct thingset_context *ts, struct thingset_endpoint *endpoint,
674 : : const char *path, size_t path_len)
675 : : {
676 : 112 : endpoint->index = THINGSET_ENDPOINT_INDEX_NONE;
677 : 112 : endpoint->use_ids = false;
678 : :
679 [ + + ]: 112 : if (path_len == 0) {
680 : 11 : endpoint->object = &root_object;
681 : 11 : return 0;
682 : : }
683 : :
684 [ + + ]: 101 : if (path[0] == '/') {
685 : 1 : return -THINGSET_ERR_NOT_A_GATEWAY;
686 : : }
687 : :
688 : : struct thingset_data_object *object =
689 : 100 : thingset_get_object_by_path(ts, path, path_len, &endpoint->index);
690 : :
691 : 100 : endpoint->object = object;
692 : :
693 [ + + ]: 100 : if (object == NULL) {
694 : 7 : return -THINGSET_ERR_NOT_FOUND;
695 : : }
696 : :
697 : 93 : return 0;
698 : : }
699 : :
700 : 43 : int thingset_endpoint_by_id(struct thingset_context *ts, struct thingset_endpoint *endpoint,
701 : : uint16_t id)
702 : : {
703 : : struct thingset_data_object *object;
704 : 43 : endpoint->index = THINGSET_ENDPOINT_INDEX_NONE;
705 : 43 : endpoint->use_ids = true;
706 : :
707 [ + + ]: 43 : if (id == 0) {
708 : 10 : endpoint->object = &root_object;
709 : 10 : return 0;
710 : : }
711 [ + + ]: 33 : else if (id == THINGSET_ID_PATHS) {
712 : 1 : endpoint->object = &paths_object;
713 : 1 : return 0;
714 : : }
715 : : #ifdef CONFIG_THINGSET_METADATA_ENDPOINT
716 [ + + ]: 32 : else if (id == THINGSET_ID_METADATA) {
717 : 4 : endpoint->object = &metadata_object;
718 : 4 : return 0;
719 : : }
720 : : #endif
721 : :
722 : 28 : object = thingset_get_object_by_id(ts, id);
723 [ + - ]: 28 : if (object != NULL) {
724 : : /* check that the found endpoint is not part of a record (cannot be queried like this) */
725 : 28 : struct thingset_data_object *parent = thingset_get_object_by_id(ts, object->parent_id);
726 [ + + - + ]: 28 : if (parent == NULL || parent->type != THINGSET_TYPE_RECORDS
727 [ # # ]: 0 : || object->type == THINGSET_TYPE_RECORDS)
728 : : {
729 : 28 : endpoint->object = object;
730 : 28 : return 0;
731 : : }
732 : : }
733 : :
734 : 0 : return -THINGSET_ERR_NOT_FOUND;
735 : : }
736 : :
737 : 84 : int thingset_get_path(struct thingset_context *ts, char *buf, size_t size,
738 : : const struct thingset_data_object *obj)
739 : : {
740 : 84 : int pos = 0;
741 [ + + ]: 84 : if (obj->parent_id != 0) {
742 : 37 : struct thingset_data_object *parent_obj = thingset_get_object_by_id(ts, obj->parent_id);
743 [ - + ]: 37 : if (parent_obj == NULL) {
744 : 0 : return -THINGSET_ERR_NOT_FOUND;
745 : : }
746 : :
747 : : /*
748 : : * Recursive implementation acceptable because the depth is automatically limited by actual
749 : : * data structure nesting depth.
750 : : */
751 : 37 : pos = thingset_get_path(ts, buf, size, parent_obj);
752 [ - + ]: 37 : if (pos < 0) {
753 : : /* propagate errors back */
754 : 0 : return pos;
755 : : }
756 : 37 : buf[pos++] = '/';
757 : : }
758 : :
759 : 84 : pos += snprintf(buf + pos, size - pos, "%s", obj->name);
760 : :
761 [ + - ]: 84 : if (pos < size) {
762 : 84 : return pos;
763 : : }
764 : : else {
765 : 0 : return -THINGSET_ERR_RESPONSE_TOO_LARGE;
766 : : }
767 : : }
768 : :
769 : 8 : static inline char *type_to_type_name(const enum thingset_type type)
770 : : {
771 : 8 : return type_name_lookup[type];
772 : : }
773 : :
774 : 2 : static int get_function_arg_types(struct thingset_context *ts, uint16_t parent_id, char *buf,
775 : : size_t size)
776 : : {
777 : 2 : int total_len = 0;
778 [ + + ]: 144 : for (unsigned int i = 0; i < ts->num_objects; i++) {
779 [ + + ]: 142 : if (ts->data_objects[i].parent_id == parent_id) {
780 : 2 : int len = 0;
781 [ + + ]: 2 : if (total_len > 0) {
782 [ - + ]: 1 : if (size < 2) {
783 : 0 : return -THINGSET_ERR_RESPONSE_TOO_LARGE;
784 : : }
785 : 1 : len += snprintf(buf, size, ",");
786 : : }
787 : 2 : char *elementType = type_to_type_name(ts->data_objects[i].type);
788 : 2 : len += snprintf(buf + len, size - len, "%s", elementType);
789 : 2 : buf += len;
790 : 2 : size -= len;
791 : 2 : total_len += len;
792 [ - + ]: 2 : if (total_len > size) {
793 : 0 : return -THINGSET_ERR_RESPONSE_TOO_LARGE;
794 : : }
795 : : }
796 : : }
797 : 2 : return total_len;
798 : : }
799 : :
800 : 8 : int thingset_get_type_name(struct thingset_context *ts, const struct thingset_data_object *obj,
801 : : char *buf, size_t size)
802 : : {
803 [ + + + ]: 8 : switch (obj->type) {
804 : 2 : case THINGSET_TYPE_ARRAY: {
805 : 2 : char *elementType = type_to_type_name(obj->data.array->element_type);
806 [ - + ]: 2 : if (sizeof(elementType) > size) {
807 : 0 : return -THINGSET_ERR_RESPONSE_TOO_LARGE;
808 : : }
809 : 2 : return snprintf(buf, size, "%s[]", elementType);
810 : : }
811 : 2 : case THINGSET_TYPE_FN_VOID:
812 : : case THINGSET_TYPE_FN_I32:
813 : 2 : snprintf(buf, size, "(");
814 : 2 : int len = 1 + get_function_arg_types(ts, obj->id, buf + 1, size - 1);
815 [ - + ]: 2 : if (len < 0) {
816 : 0 : return -THINGSET_ERR_RESPONSE_TOO_LARGE;
817 : : }
818 [ - + ]: 2 : if (size - len < 8) { /* enough space to finish? */
819 : 0 : return -THINGSET_ERR_RESPONSE_TOO_LARGE;
820 : : }
821 : 2 : buf += len;
822 : 2 : size -= len;
823 [ + + - ]: 2 : switch (obj->type) {
824 : 1 : case THINGSET_TYPE_FN_VOID:
825 : 1 : len += snprintf(buf, size, ")->()");
826 : 1 : break;
827 : 1 : case THINGSET_TYPE_FN_I32:
828 : 1 : len += snprintf(buf, size, ")->(i32)");
829 : 1 : break;
830 : 0 : default:
831 : 0 : break;
832 : : }
833 : 2 : return len;
834 : 4 : default: {
835 : 4 : char *type = type_to_type_name(obj->type);
836 : 4 : return snprintf(buf, size, "%s", type);
837 : : }
838 : : }
839 : : }
|