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