1 /* 2 * Copyright (c) 2017 Linaro Limited 3 * Copyright (c) 2018-2019 Foundries.io 4 * 5 * SPDX-License-Identifier: Apache-2.0 6 */ 7 8 /* 9 * Uses some original concepts by: 10 * Joakim Eriksson <joakime@sics.se> 11 * Niclas Finne <nfi@sics.se> 12 * Joel Hoglund <joel@sics.se> 13 */ 14 15 #define LOG_MODULE_NAME net_lwm2m_observation 16 #define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL 17 18 #include <zephyr/logging/log.h> 19 LOG_MODULE_REGISTER(LOG_MODULE_NAME); 20 21 #include "lwm2m_engine.h" 22 #include "lwm2m_object.h" 23 #include "lwm2m_util.h" 24 #include "lwm2m_rd_client.h" 25 26 #include <ctype.h> 27 #include <errno.h> 28 #include <stddef.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 33 #include <zephyr/init.h> 34 #include <zephyr/net/http/parser_url.h> 35 #include <zephyr/net/lwm2m.h> 36 #include <zephyr/net/net_ip.h> 37 #include <zephyr/net/socket.h> 38 #include <zephyr/sys/printk.h> 39 #include <zephyr/types.h> 40 #include "lwm2m_obj_server.h" 41 42 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT) 43 #include "lwm2m_rw_senml_json.h" 44 #endif 45 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT 46 #include "lwm2m_rw_json.h" 47 #endif 48 #ifdef CONFIG_LWM2M_RW_CBOR_SUPPORT 49 #include "lwm2m_rw_cbor.h" 50 #endif 51 #ifdef CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT 52 #include "lwm2m_rw_senml_cbor.h" 53 #endif 54 55 #define OBSERVE_COUNTER_START 0U 56 57 #if defined(CONFIG_COAP_EXTENDED_OPTIONS_LEN) 58 #define COAP_OPTION_BUF_LEN (CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE + 1) 59 #else 60 #define COAP_OPTION_BUF_LEN 13 61 #endif 62 63 /* Resources */ 64 static sys_slist_t obs_obj_path_list; 65 66 static struct observe_node observe_node_data[CONFIG_LWM2M_ENGINE_MAX_OBSERVER]; 67 68 /* External resources */ 69 struct lwm2m_ctx **lwm2m_sock_ctx(void); 70 71 int lwm2m_sock_nfds(void); 72 /* Resource wrappers */ lwm2m_obs_obj_path_list(void)73 sys_slist_t *lwm2m_obs_obj_path_list(void) { return &obs_obj_path_list; } 74 75 struct notification_attrs { 76 /* use to determine which value is set */ 77 double gt; 78 double lt; 79 double st; 80 int32_t pmin; 81 int32_t pmax; 82 uint8_t flags; 83 }; 84 85 /* write-attribute related definitions */ 86 static const uint8_t LWM2M_ATTR_LEN[] = {4, 4, 2, 2, 2}; 87 88 static struct lwm2m_attr write_attr_pool[CONFIG_LWM2M_NUM_ATTR]; 89 90 /* Forward declarations */ 91 92 void lwm2m_engine_free_list(sys_slist_t *path_list, sys_slist_t *free_list); 93 94 struct lwm2m_obj_path_list *lwm2m_engine_get_from_list(sys_slist_t *path_list); 95 lwm2m_attr_to_str(uint8_t type)96 const char *const lwm2m_attr_to_str(uint8_t type) 97 { 98 switch (type) { 99 case LWM2M_ATTR_PMIN: 100 return "pmin"; 101 case LWM2M_ATTR_PMAX: 102 return "pmax"; 103 case LWM2M_ATTR_GT: 104 return "gt"; 105 case LWM2M_ATTR_LT: 106 return "lt"; 107 case LWM2M_ATTR_STEP: 108 return "st"; 109 default: 110 return "unknown"; 111 } 112 } 113 update_attrs(void * ref,struct notification_attrs * out)114 static int update_attrs(void *ref, struct notification_attrs *out) 115 { 116 int i; 117 118 for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) { 119 if (ref != write_attr_pool[i].ref) { 120 continue; 121 } 122 123 switch (write_attr_pool[i].type) { 124 case LWM2M_ATTR_PMIN: 125 out->pmin = write_attr_pool[i].int_val; 126 break; 127 case LWM2M_ATTR_PMAX: 128 out->pmax = write_attr_pool[i].int_val; 129 break; 130 case LWM2M_ATTR_LT: 131 out->lt = write_attr_pool[i].float_val; 132 break; 133 case LWM2M_ATTR_GT: 134 out->gt = write_attr_pool[i].float_val; 135 break; 136 case LWM2M_ATTR_STEP: 137 out->st = write_attr_pool[i].float_val; 138 break; 139 default: 140 LOG_ERR("Unrecognized attr: %d", write_attr_pool[i].type); 141 return -EINVAL; 142 } 143 144 /* mark as set */ 145 out->flags |= BIT(write_attr_pool[i].type); 146 } 147 148 return 0; 149 } 150 clear_attrs(void * ref)151 void clear_attrs(void *ref) 152 { 153 int i; 154 155 for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) { 156 if (ref == write_attr_pool[i].ref) { 157 (void)memset(&write_attr_pool[i], 0, sizeof(write_attr_pool[i])); 158 } 159 } 160 } 161 lwm2m_observer_path_compare(const struct lwm2m_obj_path * o_p,const struct lwm2m_obj_path * p)162 static bool lwm2m_observer_path_compare(const struct lwm2m_obj_path *o_p, 163 const struct lwm2m_obj_path *p) 164 { 165 /* check obj id matched or not */ 166 if (p->obj_id != o_p->obj_id) { 167 return false; 168 } 169 170 if (o_p->level >= LWM2M_PATH_LEVEL_OBJECT_INST) { 171 if (p->level >= LWM2M_PATH_LEVEL_OBJECT_INST && 172 p->obj_inst_id != o_p->obj_inst_id) { 173 return false; 174 } 175 } 176 177 if (o_p->level >= LWM2M_PATH_LEVEL_RESOURCE) { 178 if (p->level >= LWM2M_PATH_LEVEL_RESOURCE && p->res_id != o_p->res_id) { 179 return false; 180 } 181 } 182 183 if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) && o_p->level == LWM2M_PATH_LEVEL_RESOURCE_INST) { 184 if (p->level == LWM2M_PATH_LEVEL_RESOURCE_INST && 185 p->res_inst_id != o_p->res_inst_id) { 186 return false; 187 } 188 } 189 190 return true; 191 } 192 lwm2m_notify_observer_list(sys_slist_t * path_list,const struct lwm2m_obj_path * path)193 static bool lwm2m_notify_observer_list(sys_slist_t *path_list, const struct lwm2m_obj_path *path) 194 { 195 struct lwm2m_obj_path_list *o_p; 196 197 SYS_SLIST_FOR_EACH_CONTAINER(path_list, o_p, node) { 198 if (lwm2m_observer_path_compare(&o_p->path, path)) { 199 return true; 200 } 201 } 202 203 return false; 204 } 205 lwm2m_notify_observer(uint16_t obj_id,uint16_t obj_inst_id,uint16_t res_id)206 int lwm2m_notify_observer(uint16_t obj_id, uint16_t obj_inst_id, uint16_t res_id) 207 { 208 struct lwm2m_obj_path path; 209 210 path.level = LWM2M_PATH_LEVEL_RESOURCE; 211 path.obj_id = obj_id; 212 path.obj_inst_id = obj_inst_id; 213 path.res_id = res_id; 214 215 return lwm2m_notify_observer_path(&path); 216 } 217 engine_observe_get_attributes(const struct lwm2m_obj_path * path,struct notification_attrs * attrs,uint16_t srv_obj_inst)218 static int engine_observe_get_attributes(const struct lwm2m_obj_path *path, 219 struct notification_attrs *attrs, uint16_t srv_obj_inst) 220 { 221 struct lwm2m_engine_obj *obj; 222 struct lwm2m_engine_obj_field *obj_field = NULL; 223 struct lwm2m_engine_obj_inst *obj_inst = NULL; 224 struct lwm2m_engine_res_inst *res_inst = NULL; 225 int ret, i; 226 227 /* defaults from server object */ 228 attrs->pmin = lwm2m_server_get_pmin(srv_obj_inst); 229 attrs->pmax = lwm2m_server_get_pmax(srv_obj_inst); 230 attrs->flags = BIT(LWM2M_ATTR_PMIN) | BIT(LWM2M_ATTR_PMAX); 231 232 /* check if object exists */ 233 obj = get_engine_obj(path->obj_id); 234 if (!obj) { 235 LOG_ERR("unable to find obj: %u", path->obj_id); 236 return -ENOENT; 237 } 238 239 ret = update_attrs(obj, attrs); 240 if (ret < 0) { 241 return ret; 242 } 243 244 /* check if object instance exists */ 245 if (path->level >= LWM2M_PATH_LEVEL_OBJECT_INST) { 246 obj_inst = get_engine_obj_inst(path->obj_id, path->obj_inst_id); 247 if (!obj_inst) { 248 attrs->pmax = 0; 249 attrs->pmin = 0; 250 return 0; 251 } 252 253 ret = update_attrs(obj_inst, attrs); 254 if (ret < 0) { 255 return ret; 256 } 257 } 258 259 /* check if resource exists */ 260 if (path->level >= LWM2M_PATH_LEVEL_RESOURCE) { 261 for (i = 0; i < obj_inst->resource_count; i++) { 262 if (obj_inst->resources[i].res_id == path->res_id) { 263 break; 264 } 265 } 266 267 if (i == obj_inst->resource_count) { 268 LOG_ERR("unable to find res_id: %u/%u/%u", path->obj_id, path->obj_inst_id, 269 path->res_id); 270 return -ENOENT; 271 } 272 273 /* load object field data */ 274 obj_field = lwm2m_get_engine_obj_field(obj, obj_inst->resources[i].res_id); 275 if (!obj_field) { 276 LOG_ERR("unable to find obj_field: %u/%u/%u", path->obj_id, 277 path->obj_inst_id, path->res_id); 278 return -ENOENT; 279 } 280 281 /* check for READ permission on matching resource */ 282 if (!LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) { 283 return -EPERM; 284 } 285 286 ret = update_attrs(&obj_inst->resources[i], attrs); 287 if (ret < 0) { 288 return ret; 289 } 290 } 291 292 /* check if resource instance exists */ 293 if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) && path->level == LWM2M_PATH_LEVEL_RESOURCE_INST) { 294 ret = path_to_objs(path, NULL, NULL, NULL, &res_inst); 295 if (ret < 0) { 296 return ret; 297 } 298 299 if (res_inst == NULL) { 300 return -ENOENT; 301 } 302 303 ret = update_attrs(res_inst, attrs); 304 if (ret < 0) { 305 return ret; 306 } 307 } 308 309 attrs->pmax = (attrs->pmax >= attrs->pmin) ? (uint32_t)attrs->pmax : 0UL; 310 311 return 0; 312 } 313 engine_observe_attribute_list_get(sys_slist_t * path_list,struct notification_attrs * nattrs,uint16_t server_obj_inst)314 int engine_observe_attribute_list_get(sys_slist_t *path_list, struct notification_attrs *nattrs, 315 uint16_t server_obj_inst) 316 { 317 struct lwm2m_obj_path_list *o_p; 318 /* Temporary compare values */ 319 int32_t pmin = 0; 320 int32_t pmax = 0; 321 int ret; 322 323 SYS_SLIST_FOR_EACH_CONTAINER(path_list, o_p, node) { 324 nattrs->pmin = 0; 325 nattrs->pmax = 0; 326 327 ret = engine_observe_get_attributes(&o_p->path, nattrs, server_obj_inst); 328 if (ret < 0) { 329 return ret; 330 } 331 332 if (nattrs->pmin) { 333 if (pmin == 0) { 334 pmin = nattrs->pmin; 335 } else { 336 pmin = MIN(pmin, nattrs->pmin); 337 } 338 } 339 340 if (nattrs->pmax) { 341 if (pmax == 0) { 342 pmax = nattrs->pmax; 343 } else { 344 pmax = MIN(pmax, nattrs->pmax); 345 } 346 } 347 } 348 349 nattrs->pmin = pmin; 350 nattrs->pmax = pmax; 351 return 0; 352 } 353 lwm2m_notify_observer_path(const struct lwm2m_obj_path * path)354 int lwm2m_notify_observer_path(const struct lwm2m_obj_path *path) 355 { 356 struct observe_node *obs; 357 struct notification_attrs nattrs = {0}; 358 int64_t timestamp; 359 int ret = 0; 360 int i; 361 struct lwm2m_ctx **sock_ctx = lwm2m_sock_ctx(); 362 363 if (path->level < LWM2M_PATH_LEVEL_OBJECT) { 364 return 0; 365 } 366 367 /* look for observers which match our resource */ 368 for (i = 0; i < lwm2m_sock_nfds(); ++i) { 369 SYS_SLIST_FOR_EACH_CONTAINER(&sock_ctx[i]->observer, obs, node) { 370 if (lwm2m_notify_observer_list(&obs->path_list, path)) { 371 /* update the event time for this observer */ 372 ret = engine_observe_attribute_list_get(&obs->path_list, &nattrs, 373 sock_ctx[i]->srv_obj_inst); 374 if (ret < 0) { 375 return ret; 376 } 377 378 if (nattrs.pmin) { 379 timestamp = 380 obs->last_timestamp + MSEC_PER_SEC * nattrs.pmin; 381 } else { 382 /* Trig immediately */ 383 timestamp = k_uptime_get(); 384 } 385 386 if (!obs->event_timestamp || obs->event_timestamp > timestamp) { 387 obs->resource_update = true; 388 obs->event_timestamp = timestamp; 389 } 390 391 LOG_DBG("NOTIFY EVENT %u/%u/%u", path->obj_id, path->obj_inst_id, 392 path->res_id); 393 ret++; 394 lwm2m_engine_wake_up(); 395 } 396 } 397 } 398 399 return ret; 400 } 401 engine_allocate_observer(sys_slist_t * path_list,bool composite)402 static struct observe_node *engine_allocate_observer(sys_slist_t *path_list, bool composite) 403 { 404 int i; 405 struct lwm2m_obj_path_list *entry, *tmp; 406 struct observe_node *obs = NULL; 407 408 /* find an unused observer index node */ 409 for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_OBSERVER; i++) { 410 if (!observe_node_data[i].tkl) { 411 412 obs = &observe_node_data[i]; 413 break; 414 } 415 } 416 417 if (!obs) { 418 return NULL; 419 } 420 421 sys_slist_init(&obs->path_list); 422 obs->composite = composite; 423 424 /* Allocate and copy path */ 425 SYS_SLIST_FOR_EACH_CONTAINER(path_list, tmp, node) { 426 /* Allocate path entry */ 427 entry = lwm2m_engine_get_from_list(&obs_obj_path_list); 428 if (!entry) { 429 /* Free list */ 430 lwm2m_engine_free_list(&obs->path_list, &obs_obj_path_list); 431 return NULL; 432 } 433 434 /* copy the values and add it to the list */ 435 memcpy(&entry->path, &tmp->path, sizeof(tmp->path)); 436 /* Add to last by keeping already sorted order */ 437 sys_slist_append(&obs->path_list, &entry->node); 438 } 439 440 return obs; 441 } 442 engine_observe_node_init(struct observe_node * obs,const uint8_t * token,struct lwm2m_ctx * ctx,uint8_t tkl,uint16_t format,int32_t att_pmax)443 static void engine_observe_node_init(struct observe_node *obs, const uint8_t *token, 444 struct lwm2m_ctx *ctx, uint8_t tkl, uint16_t format, 445 int32_t att_pmax) 446 { 447 struct lwm2m_obj_path_list *tmp; 448 449 memcpy(obs->token, token, tkl); 450 obs->tkl = tkl; 451 452 obs->last_timestamp = k_uptime_get(); 453 if (att_pmax) { 454 obs->event_timestamp = obs->last_timestamp + MSEC_PER_SEC * att_pmax; 455 } else { 456 obs->event_timestamp = 0; 457 } 458 obs->resource_update = false; 459 obs->active_notify = NULL; 460 obs->format = format; 461 obs->counter = OBSERVE_COUNTER_START; 462 sys_slist_append(&ctx->observer, &obs->node); 463 464 SYS_SLIST_FOR_EACH_CONTAINER(&obs->path_list, tmp, node) { 465 LOG_DBG("OBSERVER ADDED %u/%u/%u/%u(%u)", tmp->path.obj_id, tmp->path.obj_inst_id, 466 tmp->path.res_id, tmp->path.res_inst_id, tmp->path.level); 467 468 if (ctx->observe_cb) { 469 ctx->observe_cb(LWM2M_OBSERVE_EVENT_OBSERVER_ADDED, &tmp->path, ctx); 470 } 471 } 472 473 LOG_DBG("token:'%s' addr:%s", sprint_token(token, tkl), 474 lwm2m_sprint_ip_addr(&ctx->remote_addr)); 475 } 476 remove_observer_path_from_list(struct lwm2m_ctx * ctx,struct observe_node * obs,struct lwm2m_obj_path_list * o_p,sys_snode_t * prev_node)477 static void remove_observer_path_from_list(struct lwm2m_ctx *ctx, struct observe_node *obs, 478 struct lwm2m_obj_path_list *o_p, sys_snode_t *prev_node) 479 { 480 char buf[LWM2M_MAX_PATH_STR_SIZE]; 481 482 LOG_DBG("Removing observer %p for path %s", obs, lwm2m_path_log_buf(buf, &o_p->path)); 483 if (ctx->observe_cb) { 484 ctx->observe_cb(LWM2M_OBSERVE_EVENT_OBSERVER_REMOVED, &o_p->path, NULL); 485 } 486 /* Remove from the list and add to free list */ 487 sys_slist_remove(&obs->path_list, prev_node, &o_p->node); 488 sys_slist_append(&obs_obj_path_list, &o_p->node); 489 } 490 engine_observe_single_path_id_remove(struct lwm2m_ctx * ctx,struct observe_node * obs,uint16_t obj_id,int32_t obj_inst_id)491 static void engine_observe_single_path_id_remove(struct lwm2m_ctx *ctx, struct observe_node *obs, 492 uint16_t obj_id, int32_t obj_inst_id) 493 { 494 struct lwm2m_obj_path_list *o_p, *tmp; 495 sys_snode_t *prev_node = NULL; 496 497 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&obs->path_list, o_p, tmp, node) { 498 if (o_p->path.obj_id != obj_id && o_p->path.obj_inst_id) { 499 prev_node = &o_p->node; 500 continue; 501 } 502 503 if (obj_inst_id == -1 || o_p->path.obj_inst_id == obj_inst_id) { 504 remove_observer_path_from_list(ctx, obs, o_p, prev_node); 505 } else { 506 prev_node = &o_p->node; 507 } 508 } 509 } 510 engine_compare_obs_path_list(sys_slist_t * obs_path_list,sys_slist_t * path_list,int list_length)511 static bool engine_compare_obs_path_list(sys_slist_t *obs_path_list, sys_slist_t *path_list, 512 int list_length) 513 { 514 sys_snode_t *obs_ptr, *comp_ptr; 515 struct lwm2m_obj_path_list *obs_path, *comp_path; 516 517 obs_ptr = sys_slist_peek_head(obs_path_list); 518 comp_ptr = sys_slist_peek_head(path_list); 519 while (list_length--) { 520 obs_path = (struct lwm2m_obj_path_list *)obs_ptr; 521 comp_path = (struct lwm2m_obj_path_list *)comp_ptr; 522 if (memcmp(&obs_path->path, &comp_path->path, sizeof(struct lwm2m_obj_path))) { 523 return false; 524 } 525 /* Read Next Info from list entry*/ 526 obs_ptr = sys_slist_peek_next_no_check(obs_ptr); 527 comp_ptr = sys_slist_peek_next_no_check(comp_ptr); 528 } 529 530 return true; 531 } 532 engine_path_list_size(sys_slist_t * lwm2m_path_list)533 static int engine_path_list_size(sys_slist_t *lwm2m_path_list) 534 { 535 int list_size = 0; 536 struct lwm2m_obj_path_list *entry; 537 538 SYS_SLIST_FOR_EACH_CONTAINER(lwm2m_path_list, entry, node) { 539 list_size++; 540 } 541 return list_size; 542 } 543 engine_observe_node_discover(sys_slist_t * observe_node_list,sys_snode_t ** prev_node,sys_slist_t * lwm2m_path_list,const uint8_t * token,uint8_t tkl)544 struct observe_node *engine_observe_node_discover(sys_slist_t *observe_node_list, 545 sys_snode_t **prev_node, 546 sys_slist_t *lwm2m_path_list, 547 const uint8_t *token, uint8_t tkl) 548 { 549 struct observe_node *obs; 550 int obs_list_size, path_list_size = 0; 551 552 if (lwm2m_path_list) { 553 path_list_size = engine_path_list_size(lwm2m_path_list); 554 } 555 556 *prev_node = NULL; 557 558 SYS_SLIST_FOR_EACH_CONTAINER(observe_node_list, obs, node) { 559 560 if (lwm2m_path_list) { 561 /* Validate Path for discovery */ 562 obs_list_size = engine_path_list_size(&obs->path_list); 563 564 if (obs_list_size != path_list_size) { 565 *prev_node = &obs->node; 566 continue; 567 } 568 569 if (!engine_compare_obs_path_list(&obs->path_list, lwm2m_path_list, 570 obs_list_size)) { 571 *prev_node = &obs->node; 572 continue; 573 } 574 } 575 576 if (token && memcmp(obs->token, token, tkl)) { 577 /* Token not match */ 578 *prev_node = &obs->node; 579 continue; 580 } 581 return obs; 582 } 583 return NULL; 584 } 585 engine_add_observer(struct lwm2m_message * msg,const uint8_t * token,uint8_t tkl,uint16_t format)586 static int engine_add_observer(struct lwm2m_message *msg, const uint8_t *token, uint8_t tkl, 587 uint16_t format) 588 { 589 struct observe_node *obs; 590 struct notification_attrs attrs; 591 struct lwm2m_obj_path_list obs_path_list_buf; 592 sys_slist_t lwm2m_path_list; 593 sys_snode_t *prev_node = NULL; 594 int ret; 595 596 if (!msg || !msg->ctx) { 597 LOG_ERR("valid lwm2m message is required"); 598 return -EINVAL; 599 } 600 601 if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) { 602 LOG_ERR("token(%p) and token length(%u) must be valid.", token, tkl); 603 return -EINVAL; 604 } 605 606 /* Create 1 entry linked list for message path */ 607 memcpy(&obs_path_list_buf.path, &msg->path, sizeof(struct lwm2m_obj_path)); 608 sys_slist_init(&lwm2m_path_list); 609 sys_slist_append(&lwm2m_path_list, &obs_path_list_buf.node); 610 611 obs = engine_observe_node_discover(&msg->ctx->observer, &prev_node, &lwm2m_path_list, NULL, 612 0); 613 if (obs) { 614 memcpy(obs->token, token, tkl); 615 obs->tkl = tkl; 616 617 /* Cancel ongoing notification */ 618 if (obs->active_notify != NULL) { 619 lwm2m_reset_message(obs->active_notify, true); 620 obs->active_notify = NULL; 621 } 622 623 LOG_DBG("OBSERVER DUPLICATE %u/%u/%u(%u) [%s]", msg->path.obj_id, 624 msg->path.obj_inst_id, msg->path.res_id, msg->path.level, 625 lwm2m_sprint_ip_addr(&msg->ctx->remote_addr)); 626 627 return 0; 628 } 629 630 /* Read attributes and allocate new entry */ 631 ret = engine_observe_get_attributes(&msg->path, &attrs, msg->ctx->srv_obj_inst); 632 if (ret < 0) { 633 return ret; 634 } 635 636 obs = engine_allocate_observer(&lwm2m_path_list, false); 637 if (!obs) { 638 return -ENOMEM; 639 } 640 641 engine_observe_node_init(obs, token, msg->ctx, tkl, format, attrs.pmax); 642 return 0; 643 } 644 do_composite_observe_read_path_op(struct lwm2m_message * msg,uint16_t content_format,sys_slist_t * lwm2m_path_list,sys_slist_t * lwm2m_path_free_list)645 int do_composite_observe_read_path_op(struct lwm2m_message *msg, uint16_t content_format, 646 sys_slist_t *lwm2m_path_list, 647 sys_slist_t *lwm2m_path_free_list) 648 { 649 switch (content_format) { 650 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT) 651 case LWM2M_FORMAT_APP_SEML_JSON: 652 return do_composite_observe_parse_path_senml_json(msg, lwm2m_path_list, 653 lwm2m_path_free_list); 654 #endif 655 656 #if defined(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT) 657 case LWM2M_FORMAT_APP_SENML_CBOR: 658 return do_composite_observe_parse_path_senml_cbor(msg, lwm2m_path_list, 659 lwm2m_path_free_list); 660 #endif 661 662 default: 663 LOG_ERR("Unsupported content-format: %u", content_format); 664 return -ENOMSG; 665 } 666 } 667 engine_add_composite_observer(struct lwm2m_message * msg,const uint8_t * token,uint8_t tkl,uint16_t format)668 static int engine_add_composite_observer(struct lwm2m_message *msg, const uint8_t *token, 669 uint8_t tkl, uint16_t format) 670 { 671 struct observe_node *obs; 672 struct notification_attrs attrs; 673 struct lwm2m_obj_path_list lwm2m_path_list_buf[CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE]; 674 sys_slist_t lwm2m_path_list; 675 sys_slist_t lwm2m_path_free_list; 676 sys_snode_t *prev_node = NULL; 677 int ret; 678 679 if (!msg || !msg->ctx) { 680 LOG_ERR("valid lwm2m message is required"); 681 return -EINVAL; 682 } 683 684 if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) { 685 LOG_ERR("token(%p) and token length(%u) must be valid.", token, tkl); 686 return -EINVAL; 687 } 688 689 /* Init list */ 690 lwm2m_engine_path_list_init(&lwm2m_path_list, &lwm2m_path_free_list, lwm2m_path_list_buf, 691 CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE); 692 693 /* Read attributes and allocate new entry */ 694 ret = do_composite_observe_read_path_op(msg, format, &lwm2m_path_list, 695 &lwm2m_path_free_list); 696 if (ret < 0) { 697 return ret; 698 } 699 700 obs = engine_observe_node_discover(&msg->ctx->observer, &prev_node, &lwm2m_path_list, NULL, 701 0); 702 if (obs) { 703 memcpy(obs->token, token, tkl); 704 obs->tkl = tkl; 705 706 /* Cancel ongoing notification */ 707 if (obs->active_notify != NULL) { 708 lwm2m_reset_message(obs->active_notify, true); 709 obs->active_notify = NULL; 710 } 711 712 LOG_DBG("OBSERVER Composite DUPLICATE [%s]", 713 lwm2m_sprint_ip_addr(&msg->ctx->remote_addr)); 714 715 return do_composite_read_op_for_parsed_list(msg, format, &lwm2m_path_list); 716 } 717 718 ret = engine_observe_attribute_list_get(&lwm2m_path_list, &attrs, msg->ctx->srv_obj_inst); 719 if (ret < 0) { 720 return ret; 721 } 722 723 obs = engine_allocate_observer(&lwm2m_path_list, true); 724 if (!obs) { 725 return -ENOMEM; 726 } 727 engine_observe_node_init(obs, token, msg->ctx, tkl, format, attrs.pmax); 728 return do_composite_read_op_for_parsed_list(msg, format, &lwm2m_path_list); 729 } 730 remove_observer_from_list(struct lwm2m_ctx * ctx,sys_snode_t * prev_node,struct observe_node * obs)731 void remove_observer_from_list(struct lwm2m_ctx *ctx, sys_snode_t *prev_node, 732 struct observe_node *obs) 733 { 734 struct lwm2m_obj_path_list *o_p, *tmp; 735 736 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&obs->path_list, o_p, tmp, node) { 737 remove_observer_path_from_list(ctx, obs, o_p, NULL); 738 } 739 sys_slist_remove(&ctx->observer, prev_node, &obs->node); 740 (void)memset(obs, 0, sizeof(*obs)); 741 } 742 engine_remove_observer_by_token(struct lwm2m_ctx * ctx,const uint8_t * token,uint8_t tkl)743 int engine_remove_observer_by_token(struct lwm2m_ctx *ctx, const uint8_t *token, uint8_t tkl) 744 { 745 struct observe_node *obs; 746 sys_snode_t *prev_node = NULL; 747 748 if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) { 749 LOG_ERR("token(%p) and token length(%u) must be valid.", token, tkl); 750 return -EINVAL; 751 } 752 753 obs = engine_observe_node_discover(&ctx->observer, &prev_node, NULL, token, tkl); 754 if (!obs) { 755 return -ENOENT; 756 } 757 758 remove_observer_from_list(ctx, prev_node, obs); 759 760 LOG_DBG("observer '%s' removed", sprint_token(token, tkl)); 761 762 return 0; 763 } 764 engine_remove_composite_observer(struct lwm2m_message * msg,const uint8_t * token,uint8_t tkl,uint16_t format)765 static int engine_remove_composite_observer(struct lwm2m_message *msg, const uint8_t *token, 766 uint8_t tkl, uint16_t format) 767 { 768 struct observe_node *obs; 769 sys_snode_t *prev_node = NULL; 770 struct lwm2m_obj_path_list lwm2m_path_list_buf[CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE]; 771 sys_slist_t lwm2m_path_list; 772 sys_slist_t lwm2m_path_free_list; 773 int ret; 774 775 if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) { 776 LOG_ERR("token(%p) and token length(%u) must be valid.", token, tkl); 777 return -EINVAL; 778 } 779 780 /* Init list */ 781 lwm2m_engine_path_list_init(&lwm2m_path_list, &lwm2m_path_free_list, lwm2m_path_list_buf, 782 CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE); 783 784 ret = do_composite_observe_read_path_op(msg, format, &lwm2m_path_list, 785 &lwm2m_path_free_list); 786 if (ret < 0) { 787 return ret; 788 } 789 790 obs = engine_observe_node_discover(&msg->ctx->observer, &prev_node, &lwm2m_path_list, token, 791 tkl); 792 if (!obs) { 793 return -ENOENT; 794 } 795 796 remove_observer_from_list(msg->ctx, prev_node, obs); 797 798 LOG_DBG("observer '%s' removed", sprint_token(token, tkl)); 799 800 return do_composite_read_op_for_parsed_list(msg, format, &lwm2m_path_list); 801 } 802 803 #if defined(CONFIG_LOG) lwm2m_path_log_buf(char * buf,struct lwm2m_obj_path * path)804 char *lwm2m_path_log_buf(char *buf, struct lwm2m_obj_path *path) 805 { 806 size_t cur; 807 808 if (!path) { 809 sprintf(buf, "/"); 810 return buf; 811 } 812 813 cur = sprintf(buf, "%u", path->obj_id); 814 815 if (path->level > 1) { 816 cur += sprintf(buf + cur, "/%u", path->obj_inst_id); 817 } 818 if (path->level > 2) { 819 cur += sprintf(buf + cur, "/%u", path->res_id); 820 } 821 if (path->level > 3) { 822 cur += sprintf(buf + cur, "/%u", path->res_inst_id); 823 } 824 825 return buf; 826 } 827 #endif /* CONFIG_LOG */ 828 829 #if defined(CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH) engine_remove_observer_by_path(struct lwm2m_ctx * ctx,struct lwm2m_obj_path * path)830 static int engine_remove_observer_by_path(struct lwm2m_ctx *ctx, struct lwm2m_obj_path *path) 831 { 832 char buf[LWM2M_MAX_PATH_STR_SIZE]; 833 struct observe_node *obs; 834 struct lwm2m_obj_path_list obs_path_list_buf; 835 sys_slist_t lwm2m_path_list; 836 sys_snode_t *prev_node = NULL; 837 838 /* Create 1 entry linked list for message path */ 839 memcpy(&obs_path_list_buf.path, path, sizeof(struct lwm2m_obj_path)); 840 sys_slist_init(&lwm2m_path_list); 841 sys_slist_append(&lwm2m_path_list, &obs_path_list_buf.node); 842 843 obs = engine_observe_node_discover(&ctx->observer, &prev_node, &lwm2m_path_list, NULL, 0); 844 if (!obs) { 845 return -ENOENT; 846 } 847 848 LOG_INF("Removing observer for path %s", lwm2m_path_log_buf(buf, path)); 849 850 remove_observer_from_list(ctx, prev_node, obs); 851 852 return 0; 853 } 854 #endif /* CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH */ 855 engine_remove_observer_by_id(uint16_t obj_id,int32_t obj_inst_id)856 void engine_remove_observer_by_id(uint16_t obj_id, int32_t obj_inst_id) 857 { 858 struct observe_node *obs, *tmp; 859 sys_snode_t *prev_node = NULL; 860 int i; 861 struct lwm2m_ctx **sock_ctx = lwm2m_sock_ctx(); 862 863 /* remove observer instances accordingly */ 864 for (i = 0; i < lwm2m_sock_nfds(); ++i) { 865 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&sock_ctx[i]->observer, obs, tmp, node) { 866 engine_observe_single_path_id_remove(sock_ctx[i], obs, obj_id, obj_inst_id); 867 868 if (sys_slist_is_empty(&obs->path_list)) { 869 remove_observer_from_list(sock_ctx[i], prev_node, obs); 870 } else { 871 prev_node = &obs->node; 872 } 873 } 874 } 875 } 876 lwm2m_update_or_allocate_attribute(void * ref,uint8_t type,void * data)877 static int lwm2m_update_or_allocate_attribute(void *ref, uint8_t type, void *data) 878 { 879 struct lwm2m_attr *attr; 880 int i; 881 882 /* find matching attributes */ 883 for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) { 884 if (ref != write_attr_pool[i].ref || write_attr_pool[i].type != type) { 885 continue; 886 } 887 888 attr = write_attr_pool + i; 889 type = attr->type; 890 891 if (type <= LWM2M_ATTR_PMAX) { 892 attr->int_val = *(int32_t *)data; 893 LOG_DBG("Update %s to %d", lwm2m_attr_to_str(type), attr->int_val); 894 } else { 895 attr->float_val = *(double *)data; 896 LOG_DBG("Update %s to %f", lwm2m_attr_to_str(type), attr->float_val); 897 } 898 return 0; 899 } 900 901 /* add attribute to obj/obj_inst/res/res_inst */ 902 /* grab an entry for newly added attribute */ 903 for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) { 904 if (!write_attr_pool[i].ref) { 905 break; 906 } 907 } 908 909 if (i == CONFIG_LWM2M_NUM_ATTR) { 910 return -ENOMEM; 911 } 912 913 attr = write_attr_pool + i; 914 attr->type = type; 915 attr->ref = ref; 916 917 if (type <= LWM2M_ATTR_PMAX) { 918 attr->int_val = *(int32_t *)data; 919 LOG_DBG("Add %s to %d", lwm2m_attr_to_str(type), attr->int_val); 920 } else { 921 attr->float_val = *(double *)data; 922 LOG_DBG("Add %s to %f", lwm2m_attr_to_str(type), attr->float_val); 923 } 924 return 0; 925 } 926 lwm2m_engine_get_attr_name(const struct lwm2m_attr * attr)927 const char *lwm2m_engine_get_attr_name(const struct lwm2m_attr *attr) 928 { 929 if (attr->type >= NR_LWM2M_ATTR) { 930 return NULL; 931 } 932 933 return lwm2m_attr_to_str(attr->type); 934 } 935 lwm2m_engine_observer_timestamp_update(sys_slist_t * observer,const struct lwm2m_obj_path * path,uint16_t srv_obj_inst)936 static int lwm2m_engine_observer_timestamp_update(sys_slist_t *observer, 937 const struct lwm2m_obj_path *path, 938 uint16_t srv_obj_inst) 939 { 940 struct observe_node *obs; 941 struct notification_attrs nattrs = {0}; 942 int ret; 943 int64_t timestamp; 944 945 /* update observe_node accordingly */ 946 SYS_SLIST_FOR_EACH_CONTAINER(observer, obs, node) { 947 if (obs->resource_update) { 948 /* Resource Update on going skip this*/ 949 continue; 950 } 951 /* Compare Observation node path to updated one */ 952 if (!lwm2m_notify_observer_list(&obs->path_list, path)) { 953 continue; 954 } 955 956 /* Read Attributes after validation Path */ 957 ret = engine_observe_attribute_list_get(&obs->path_list, &nattrs, srv_obj_inst); 958 if (ret < 0) { 959 return ret; 960 } 961 962 /* Update based on by PMax */ 963 if (nattrs.pmax) { 964 /* Update Current */ 965 timestamp = obs->last_timestamp + MSEC_PER_SEC * nattrs.pmax; 966 } else { 967 /* Disable Automatic Notify */ 968 timestamp = 0; 969 } 970 obs->event_timestamp = timestamp; 971 972 (void)memset(&nattrs, 0, sizeof(nattrs)); 973 } 974 return 0; 975 } 976 977 /* input / output selection */ 978 lwm2m_get_path_reference_ptr(struct lwm2m_engine_obj * obj,const struct lwm2m_obj_path * path,void ** ref)979 int lwm2m_get_path_reference_ptr(struct lwm2m_engine_obj *obj, const struct lwm2m_obj_path *path, 980 void **ref) 981 { 982 struct lwm2m_engine_obj_inst *obj_inst; 983 struct lwm2m_engine_res *res; 984 struct lwm2m_engine_res_inst *res_inst; 985 int ret; 986 987 if (!obj) { 988 /* Discover Object */ 989 obj = get_engine_obj(path->obj_id); 990 if (!obj) { 991 /* No matching object found - ignore request */ 992 return -ENOENT; 993 } 994 } 995 996 if (path->level == LWM2M_PATH_LEVEL_OBJECT) { 997 *ref = obj; 998 } else if (path->level == LWM2M_PATH_LEVEL_OBJECT_INST) { 999 obj_inst = get_engine_obj_inst(path->obj_id, path->obj_inst_id); 1000 if (!obj_inst) { 1001 return -ENOENT; 1002 } 1003 1004 *ref = obj_inst; 1005 } else if (path->level == LWM2M_PATH_LEVEL_RESOURCE) { 1006 ret = path_to_objs(path, NULL, NULL, &res, NULL); 1007 if (ret < 0) { 1008 return ret; 1009 } 1010 1011 *ref = res; 1012 } else if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) && 1013 path->level == LWM2M_PATH_LEVEL_RESOURCE_INST) { 1014 1015 ret = path_to_objs(path, NULL, NULL, NULL, &res_inst); 1016 if (ret < 0) { 1017 return ret; 1018 } 1019 1020 *ref = res_inst; 1021 } else { 1022 /* bad request */ 1023 return -EEXIST; 1024 } 1025 1026 return 0; 1027 } 1028 lwm2m_update_observer_min_period(struct lwm2m_ctx * client_ctx,const struct lwm2m_obj_path * path,uint32_t period_s)1029 int lwm2m_update_observer_min_period(struct lwm2m_ctx *client_ctx, 1030 const struct lwm2m_obj_path *path, uint32_t period_s) 1031 { 1032 int ret; 1033 struct notification_attrs nattrs = {0}; 1034 struct notification_attrs attrs = {0}; 1035 void *ref; 1036 1037 /* Read Reference pointer to attribute */ 1038 ret = lwm2m_get_path_reference_ptr(NULL, path, &ref); 1039 if (ret < 0) { 1040 return ret; 1041 } 1042 /* retrieve existing attributes */ 1043 ret = update_attrs(ref, &nattrs); 1044 if (ret < 0) { 1045 return ret; 1046 } 1047 1048 if (nattrs.flags & BIT(LWM2M_ATTR_PMIN) && nattrs.pmin == period_s) { 1049 /* No need to change */ 1050 return 0; 1051 } 1052 1053 /* Read Pmin & Pmax values for path */ 1054 ret = engine_observe_get_attributes(path, &attrs, client_ctx->srv_obj_inst); 1055 if (ret < 0) { 1056 return ret; 1057 } 1058 1059 if (period_s && attrs.pmax && attrs.pmax < period_s) { 1060 LOG_DBG("New pmin (%d) > pmax (%d)", period_s, attrs.pmax); 1061 return -EEXIST; 1062 } 1063 1064 return lwm2m_update_or_allocate_attribute(ref, LWM2M_ATTR_PMIN, &period_s); 1065 } 1066 lwm2m_update_observer_max_period(struct lwm2m_ctx * client_ctx,const struct lwm2m_obj_path * path,uint32_t period_s)1067 int lwm2m_update_observer_max_period(struct lwm2m_ctx *client_ctx, 1068 const struct lwm2m_obj_path *path, uint32_t period_s) 1069 { 1070 int ret; 1071 void *ref; 1072 struct notification_attrs nattrs = {0}; 1073 struct notification_attrs attrs = {0}; 1074 1075 /* Read Reference pointer to attribute */ 1076 ret = lwm2m_get_path_reference_ptr(NULL, path, &ref); 1077 if (ret < 0) { 1078 return ret; 1079 } 1080 /* retrieve existing attributes */ 1081 ret = update_attrs(ref, &nattrs); 1082 if (ret < 0) { 1083 return ret; 1084 } 1085 1086 if (nattrs.flags & BIT(LWM2M_ATTR_PMAX) && nattrs.pmax == period_s) { 1087 /* No need to change */ 1088 return 0; 1089 } 1090 1091 /* Read Pmin & Pmax values for path */ 1092 ret = engine_observe_get_attributes(path, &attrs, client_ctx->srv_obj_inst); 1093 if (ret < 0) { 1094 return ret; 1095 } 1096 1097 if (period_s && attrs.pmin > period_s) { 1098 LOG_DBG("pmin (%d) > new pmax (%d)", attrs.pmin, period_s); 1099 return -EEXIST; 1100 } 1101 1102 /* Update or allocate new */ 1103 ret = lwm2m_update_or_allocate_attribute(ref, LWM2M_ATTR_PMAX, &period_s); 1104 if (ret < 0) { 1105 return ret; 1106 } 1107 1108 /* Update Observer timestamp */ 1109 return lwm2m_engine_observer_timestamp_update(&client_ctx->observer, path, 1110 client_ctx->srv_obj_inst); 1111 } 1112 lwm2m_engine_get_next_attr(const void * ref,struct lwm2m_attr * prev)1113 struct lwm2m_attr *lwm2m_engine_get_next_attr(const void *ref, struct lwm2m_attr *prev) 1114 { 1115 struct lwm2m_attr *iter = (prev == NULL) ? write_attr_pool : prev + 1; 1116 struct lwm2m_attr *result = NULL; 1117 1118 if (!PART_OF_ARRAY(write_attr_pool, iter)) { 1119 return NULL; 1120 } 1121 1122 while (iter < &write_attr_pool[ARRAY_SIZE(write_attr_pool)]) { 1123 if (ref == iter->ref) { 1124 result = iter; 1125 break; 1126 } 1127 1128 ++iter; 1129 } 1130 1131 return result; 1132 } 1133 lwm2m_write_attr_handler(struct lwm2m_engine_obj * obj,struct lwm2m_message * msg)1134 int lwm2m_write_attr_handler(struct lwm2m_engine_obj *obj, struct lwm2m_message *msg) 1135 { 1136 bool update_observe_node = false; 1137 char opt_buf[COAP_OPTION_BUF_LEN]; 1138 int nr_opt, i, ret = 0; 1139 struct coap_option options[NR_LWM2M_ATTR]; 1140 struct lwm2m_attr *attr; 1141 struct notification_attrs nattrs = {0}; 1142 uint8_t type = 0U; 1143 void *nattr_ptrs[NR_LWM2M_ATTR] = {&nattrs.pmin, &nattrs.pmax, &nattrs.gt, &nattrs.lt, 1144 &nattrs.st}; 1145 void *ref; 1146 1147 if (!obj || !msg) { 1148 return -EINVAL; 1149 } 1150 1151 /* do not expose security obj */ 1152 if (obj->obj_id == LWM2M_OBJECT_SECURITY_ID) { 1153 return -ENOENT; 1154 } 1155 1156 nr_opt = coap_find_options(msg->in.in_cpkt, COAP_OPTION_URI_QUERY, options, NR_LWM2M_ATTR); 1157 if (nr_opt <= 0) { 1158 LOG_ERR("No attribute found!"); 1159 /* translate as bad request */ 1160 return -EEXIST; 1161 } 1162 1163 /* get lwm2m_attr slist */ 1164 ret = lwm2m_get_path_reference_ptr(obj, &msg->path, &ref); 1165 if (ret < 0) { 1166 return ret; 1167 } 1168 1169 /* retrieve existing attributes */ 1170 ret = update_attrs(ref, &nattrs); 1171 if (ret < 0) { 1172 return ret; 1173 } 1174 1175 /* loop through options to parse attribute */ 1176 for (i = 0; i < nr_opt; i++) { 1177 int limit = MIN(options[i].len, 5), plen = 0, vlen; 1178 struct lwm2m_attr val = {0}; 1179 1180 type = 0U; 1181 1182 /* search for '=' */ 1183 while (plen < limit && options[i].value[plen] != '=') { 1184 plen += 1; 1185 } 1186 1187 /* either length = 2(gt/lt/st) or = 4(pmin/pmax) */ 1188 if (plen != 2 && plen != 4) { 1189 continue; 1190 } 1191 1192 /* matching attribute name */ 1193 for (type = 0U; type < NR_LWM2M_ATTR; type++) { 1194 if (LWM2M_ATTR_LEN[type] == plen && 1195 !memcmp(options[i].value, lwm2m_attr_to_str(type), 1196 LWM2M_ATTR_LEN[type])) { 1197 break; 1198 } 1199 } 1200 1201 /* unrecognized attribute */ 1202 if (type == NR_LWM2M_ATTR) { 1203 continue; 1204 } 1205 1206 /* unset attribute when no value's given */ 1207 if (options[i].len == plen) { 1208 nattrs.flags &= ~BIT(type); 1209 1210 (void)memset(nattr_ptrs[type], 0, 1211 type <= LWM2M_ATTR_PMAX ? sizeof(int32_t) : sizeof(double)); 1212 continue; 1213 } 1214 1215 /* gt/lt/st cannot be assigned to obj/obj_inst unless unset */ 1216 if (plen == 2 && msg->path.level <= 2U) { 1217 return -EEXIST; 1218 } 1219 1220 vlen = options[i].len - plen - 1; 1221 memcpy(opt_buf, options[i].value + plen + 1, vlen); 1222 opt_buf[vlen] = '\0'; 1223 1224 /* convert value to integer or float */ 1225 if (plen == 4) { 1226 char *end; 1227 long v; 1228 1229 /* pmin/pmax: integer (sec 5.1.2) 1230 * however, negative is non-sense 1231 */ 1232 errno = 0; 1233 v = strtol(opt_buf, &end, 10); 1234 if (errno || *end || v < 0) { 1235 ret = -EINVAL; 1236 } 1237 1238 val.int_val = v; 1239 } else { 1240 /* gt/lt/st: type float */ 1241 ret = lwm2m_atof(opt_buf, &val.float_val); 1242 } 1243 1244 if (ret < 0) { 1245 LOG_ERR("invalid attr[%s] value", lwm2m_attr_to_str(type)); 1246 /* bad request */ 1247 return -EEXIST; 1248 } 1249 1250 if (type <= LWM2M_ATTR_PMAX) { 1251 *(int32_t *)nattr_ptrs[type] = val.int_val; 1252 } else { 1253 memcpy(nattr_ptrs[type], &val.float_val, sizeof(val.float_val)); 1254 } 1255 1256 nattrs.flags |= BIT(type); 1257 } 1258 1259 if (((nattrs.flags & (BIT(LWM2M_ATTR_PMIN) | BIT(LWM2M_ATTR_PMAX))) == 1260 (BIT(LWM2M_ATTR_PMIN) | BIT(LWM2M_ATTR_PMAX))) && 1261 nattrs.pmin > nattrs.pmax) { 1262 LOG_DBG("pmin (%d) > pmax (%d)", nattrs.pmin, nattrs.pmax); 1263 return -EEXIST; 1264 } 1265 1266 if ((nattrs.flags & (BIT(LWM2M_ATTR_LT) | BIT(LWM2M_ATTR_GT))) == 1267 (BIT(LWM2M_ATTR_LT) | BIT(LWM2M_ATTR_GT))) { 1268 if (nattrs.lt > nattrs.gt) { 1269 LOG_DBG("lt > gt"); 1270 return -EEXIST; 1271 } 1272 1273 if (nattrs.flags & BIT(LWM2M_ATTR_STEP)) { 1274 if (nattrs.lt + 2 * nattrs.st > nattrs.gt) { 1275 LOG_DBG("lt + 2*st > gt"); 1276 return -EEXIST; 1277 } 1278 } 1279 } 1280 1281 /* find matching attributes */ 1282 for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) { 1283 if (ref != write_attr_pool[i].ref) { 1284 continue; 1285 } 1286 1287 attr = write_attr_pool + i; 1288 type = attr->type; 1289 1290 if (!(BIT(type) & nattrs.flags)) { 1291 LOG_DBG("Unset attr %s", lwm2m_attr_to_str(type)); 1292 (void)memset(attr, 0, sizeof(*attr)); 1293 1294 if (type <= LWM2M_ATTR_PMAX) { 1295 update_observe_node = true; 1296 } 1297 1298 continue; 1299 } 1300 1301 nattrs.flags &= ~BIT(type); 1302 1303 if (type <= LWM2M_ATTR_PMAX) { 1304 if (attr->int_val == *(int32_t *)nattr_ptrs[type]) { 1305 continue; 1306 } 1307 1308 attr->int_val = *(int32_t *)nattr_ptrs[type]; 1309 update_observe_node = true; 1310 1311 LOG_DBG("Update %s to %d", lwm2m_attr_to_str(type), attr->int_val); 1312 } else { 1313 if (attr->float_val == *(double *)nattr_ptrs[type]) { 1314 continue; 1315 } 1316 1317 attr->float_val = *(double *)nattr_ptrs[type]; 1318 1319 LOG_DBG("Update %s to %f", lwm2m_attr_to_str(type), attr->float_val); 1320 } 1321 } 1322 1323 /* add attribute to obj/obj_inst/res/res_inst */ 1324 for (type = 0U; nattrs.flags && type < NR_LWM2M_ATTR; type++) { 1325 if (!(BIT(type) & nattrs.flags)) { 1326 continue; 1327 } 1328 1329 /* grab an entry for newly added attribute */ 1330 for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) { 1331 if (!write_attr_pool[i].ref) { 1332 break; 1333 } 1334 } 1335 1336 if (i == CONFIG_LWM2M_NUM_ATTR) { 1337 return -ENOMEM; 1338 } 1339 1340 attr = write_attr_pool + i; 1341 attr->type = type; 1342 attr->ref = ref; 1343 1344 if (type <= LWM2M_ATTR_PMAX) { 1345 attr->int_val = *(int32_t *)nattr_ptrs[type]; 1346 update_observe_node = true; 1347 1348 LOG_DBG("Add %s to %d", lwm2m_attr_to_str(type), attr->int_val); 1349 } else { 1350 attr->float_val = *(double *)nattr_ptrs[type]; 1351 1352 LOG_DBG("Add %s to %f", lwm2m_attr_to_str(type), attr->float_val); 1353 } 1354 1355 nattrs.flags &= ~BIT(type); 1356 } 1357 1358 /* check only pmin/pmax */ 1359 if (!update_observe_node) { 1360 return 0; 1361 } 1362 1363 lwm2m_engine_observer_timestamp_update(&msg->ctx->observer, &msg->path, 1364 msg->ctx->srv_obj_inst); 1365 1366 return 0; 1367 } 1368 lwm2m_path_is_observed(const struct lwm2m_obj_path * path)1369 bool lwm2m_path_is_observed(const struct lwm2m_obj_path *path) 1370 { 1371 int i; 1372 struct observe_node *obs; 1373 struct lwm2m_ctx **sock_ctx = lwm2m_sock_ctx(); 1374 1375 for (i = 0; i < lwm2m_sock_nfds(); ++i) { 1376 SYS_SLIST_FOR_EACH_CONTAINER(&sock_ctx[i]->observer, obs, node) { 1377 1378 if (lwm2m_notify_observer_list(&obs->path_list, path)) { 1379 return true; 1380 } 1381 } 1382 } 1383 return false; 1384 } 1385 lwm2m_engine_observation_handler(struct lwm2m_message * msg,int observe,uint16_t accept,bool composite)1386 int lwm2m_engine_observation_handler(struct lwm2m_message *msg, int observe, uint16_t accept, 1387 bool composite) 1388 { 1389 int r; 1390 1391 if (observe == 0) { 1392 /* add new observer */ 1393 r = coap_append_option_int(msg->out.out_cpkt, COAP_OPTION_OBSERVE, 1394 OBSERVE_COUNTER_START); 1395 if (r < 0) { 1396 LOG_ERR("OBSERVE option error: %d", r); 1397 return r; 1398 } 1399 1400 if (composite) { 1401 r = engine_add_composite_observer(msg, msg->token, msg->tkl, accept); 1402 } else { 1403 r = engine_add_observer(msg, msg->token, msg->tkl, accept); 1404 } 1405 1406 if (r < 0) { 1407 LOG_ERR("add OBSERVE error: %d", r); 1408 } 1409 } else if (observe == 1) { 1410 /* remove observer */ 1411 if (composite) { 1412 r = engine_remove_composite_observer(msg, msg->token, msg->tkl, accept); 1413 } else { 1414 r = engine_remove_observer_by_token(msg->ctx, msg->token, msg->tkl); 1415 if (r < 0) { 1416 #if defined(CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH) 1417 r = engine_remove_observer_by_path(msg->ctx, &msg->path); 1418 if (r < 0) 1419 #endif /* CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH */ 1420 { 1421 LOG_ERR("remove observe error: %d", r); 1422 r = 0; 1423 } 1424 } 1425 } 1426 1427 } else { 1428 r = -EINVAL; 1429 } 1430 return r; 1431 } 1432 engine_observe_shedule_next_event(struct observe_node * obs,uint16_t srv_obj_inst,const int64_t timestamp)1433 int64_t engine_observe_shedule_next_event(struct observe_node *obs, uint16_t srv_obj_inst, 1434 const int64_t timestamp) 1435 { 1436 struct notification_attrs attrs; 1437 int64_t t_s = 0; 1438 int ret; 1439 1440 ret = engine_observe_attribute_list_get(&obs->path_list, &attrs, srv_obj_inst); 1441 if (ret < 0) { 1442 return 0; 1443 } 1444 1445 if (attrs.pmax) { 1446 t_s = timestamp + MSEC_PER_SEC * attrs.pmax; 1447 } 1448 1449 return t_s; 1450 } 1451 lwm2m_engine_get_from_list(sys_slist_t * path_list)1452 struct lwm2m_obj_path_list *lwm2m_engine_get_from_list(sys_slist_t *path_list) 1453 { 1454 sys_snode_t *path_node = sys_slist_get(path_list); 1455 struct lwm2m_obj_path_list *entry; 1456 1457 if (!path_node) { 1458 return NULL; 1459 } 1460 1461 entry = SYS_SLIST_CONTAINER(path_node, entry, node); 1462 if (entry) { 1463 memset(entry, 0, sizeof(struct lwm2m_obj_path_list)); 1464 } 1465 return entry; 1466 } 1467 lwm2m_engine_free_list(sys_slist_t * path_list,sys_slist_t * free_list)1468 void lwm2m_engine_free_list(sys_slist_t *path_list, sys_slist_t *free_list) 1469 { 1470 sys_snode_t *node; 1471 1472 while (NULL != (node = sys_slist_get(path_list))) { 1473 /* Add to free list */ 1474 sys_slist_append(free_list, node); 1475 } 1476 } 1477 lwm2m_engine_path_list_init(sys_slist_t * lwm2m_path_list,sys_slist_t * lwm2m_free_list,struct lwm2m_obj_path_list path_object_buf[],uint8_t path_object_size)1478 void lwm2m_engine_path_list_init(sys_slist_t *lwm2m_path_list, sys_slist_t *lwm2m_free_list, 1479 struct lwm2m_obj_path_list path_object_buf[], 1480 uint8_t path_object_size) 1481 { 1482 /* Init list */ 1483 sys_slist_init(lwm2m_path_list); 1484 sys_slist_init(lwm2m_free_list); 1485 1486 /* Put buffer elements to free list */ 1487 for (int i = 0; i < path_object_size; i++) { 1488 sys_slist_append(lwm2m_free_list, &path_object_buf[i].node); 1489 } 1490 } 1491 lwm2m_engine_add_path_to_list(sys_slist_t * lwm2m_path_list,sys_slist_t * lwm2m_free_list,const struct lwm2m_obj_path * path)1492 int lwm2m_engine_add_path_to_list(sys_slist_t *lwm2m_path_list, sys_slist_t *lwm2m_free_list, 1493 const struct lwm2m_obj_path *path) 1494 { 1495 struct lwm2m_obj_path_list *prev = NULL; 1496 struct lwm2m_obj_path_list *entry; 1497 struct lwm2m_obj_path_list *new_entry; 1498 bool add_before_current = false; 1499 1500 if (path->level == LWM2M_PATH_LEVEL_NONE) { 1501 /* Clear the list if we are adding the root path which includes all */ 1502 lwm2m_engine_free_list(lwm2m_path_list, lwm2m_free_list); 1503 } 1504 1505 /* Check is it at list already here */ 1506 new_entry = lwm2m_engine_get_from_list(lwm2m_free_list); 1507 if (!new_entry) { 1508 return -1; 1509 } 1510 1511 new_entry->path = *path; 1512 if (!sys_slist_is_empty(lwm2m_path_list)) { 1513 1514 /* Keep list Ordered by Object ID / Object instance / resource ID / 1515 * Resource Instance ID 1516 */ 1517 SYS_SLIST_FOR_EACH_CONTAINER(lwm2m_path_list, entry, node) { 1518 if (entry->path.level == LWM2M_PATH_LEVEL_NONE || 1519 lwm2m_obj_path_equal(&entry->path, &new_entry->path)) { 1520 /* Already Root request at list or current path is at list */ 1521 sys_slist_append(lwm2m_free_list, &new_entry->node); 1522 return 0; 1523 } 1524 1525 /* 1526 * algorithm assumes that list is already properly sorted and that 1527 * there are no duplicates. general idea: 1528 * - if at any level up to new entry's path level, IDs below the level 1529 * match and the ID of the new entry at that level is smaller, 1530 * insert before. 1531 * - if all IDs of the new entry match the existing entry, insert before. 1532 * Because of sorting and no duplicates, the existing entry must have 1533 * higher path level and come after the new entry. 1534 */ 1535 switch (new_entry->path.level) { 1536 case LWM2M_PATH_LEVEL_OBJECT: 1537 add_before_current = new_entry->path.obj_id <= entry->path.obj_id; 1538 break; 1539 1540 case LWM2M_PATH_LEVEL_OBJECT_INST: 1541 add_before_current = 1542 (new_entry->path.obj_id < entry->path.obj_id) || 1543 1544 (entry->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST && 1545 entry->path.obj_id == new_entry->path.obj_id && 1546 new_entry->path.obj_inst_id <= entry->path.obj_inst_id); 1547 break; 1548 1549 case LWM2M_PATH_LEVEL_RESOURCE: 1550 add_before_current = 1551 (new_entry->path.obj_id < entry->path.obj_id) || 1552 1553 (entry->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST && 1554 entry->path.obj_id == new_entry->path.obj_id && 1555 new_entry->path.obj_inst_id < entry->path.obj_inst_id) || 1556 1557 (entry->path.level >= LWM2M_PATH_LEVEL_RESOURCE && 1558 entry->path.obj_id == new_entry->path.obj_id && 1559 entry->path.obj_inst_id == new_entry->path.obj_inst_id && 1560 new_entry->path.res_id <= entry->path.res_id); 1561 break; 1562 1563 case LWM2M_PATH_LEVEL_RESOURCE_INST: 1564 add_before_current = 1565 (new_entry->path.obj_id < entry->path.obj_id) || 1566 1567 (entry->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST && 1568 entry->path.obj_id == new_entry->path.obj_id && 1569 new_entry->path.obj_inst_id < entry->path.obj_inst_id) || 1570 1571 (entry->path.level >= LWM2M_PATH_LEVEL_RESOURCE && 1572 entry->path.obj_id == new_entry->path.obj_id && 1573 entry->path.obj_inst_id == new_entry->path.obj_inst_id && 1574 new_entry->path.res_id < entry->path.res_id) || 1575 1576 (entry->path.level >= LWM2M_PATH_LEVEL_RESOURCE_INST && 1577 entry->path.obj_id == new_entry->path.obj_id && 1578 entry->path.obj_inst_id == new_entry->path.obj_inst_id && 1579 entry->path.res_id == new_entry->path.res_id && 1580 new_entry->path.res_inst_id <= entry->path.res_inst_id); 1581 break; 1582 } 1583 1584 if (add_before_current) { 1585 if (prev) { 1586 sys_slist_insert(lwm2m_path_list, &prev->node, 1587 &new_entry->node); 1588 } else { 1589 sys_slist_prepend(lwm2m_path_list, &new_entry->node); 1590 } 1591 return 0; 1592 } 1593 prev = entry; 1594 } 1595 } 1596 1597 /* Add First or new tail entry */ 1598 sys_slist_append(lwm2m_path_list, &new_entry->node); 1599 return 0; 1600 } 1601 lwm2m_engine_clear_duplicate_path(sys_slist_t * lwm2m_path_list,sys_slist_t * lwm2m_free_list)1602 void lwm2m_engine_clear_duplicate_path(sys_slist_t *lwm2m_path_list, sys_slist_t *lwm2m_free_list) 1603 { 1604 struct lwm2m_obj_path_list *prev = NULL; 1605 struct lwm2m_obj_path_list *entry, *tmp; 1606 bool remove_entry; 1607 1608 if (sys_slist_is_empty(lwm2m_path_list)) { 1609 return; 1610 } 1611 1612 /* Keep list Ordered but remove if shorter path is similar */ 1613 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(lwm2m_path_list, entry, tmp, node) { 1614 1615 if (prev && prev->path.level < entry->path.level) { 1616 if (prev->path.level == LWM2M_PATH_LEVEL_OBJECT && 1617 entry->path.obj_id == prev->path.obj_id) { 1618 remove_entry = true; 1619 } else if (prev->path.level == LWM2M_PATH_LEVEL_OBJECT_INST && 1620 entry->path.obj_id == prev->path.obj_id && 1621 entry->path.obj_inst_id == prev->path.obj_inst_id) { 1622 /* Remove current from the list */ 1623 remove_entry = true; 1624 } else if (prev->path.level == LWM2M_PATH_LEVEL_RESOURCE && 1625 entry->path.obj_id == prev->path.obj_id && 1626 entry->path.obj_inst_id == prev->path.obj_inst_id && 1627 entry->path.res_id == prev->path.res_id) { 1628 /* Remove current from the list */ 1629 remove_entry = true; 1630 } else { 1631 remove_entry = false; 1632 } 1633 1634 if (remove_entry) { 1635 /* Remove Current entry */ 1636 sys_slist_remove(lwm2m_path_list, &prev->node, &entry->node); 1637 sys_slist_append(lwm2m_free_list, &entry->node); 1638 } else { 1639 prev = entry; 1640 } 1641 } else { 1642 prev = entry; 1643 } 1644 } 1645 } 1646