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