1 /*
2  * Copyright (c) 2022 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #define LOG_MODULE_NAME net_lwm2m_shell
7 #define LOG_LEVEL	CONFIG_LWM2M_LOG_LEVEL
8 
9 #include <zephyr/logging/log.h>
10 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
11 
12 #include <stddef.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 
17 #include <zephyr/kernel.h>
18 #include <zephyr/net/lwm2m.h>
19 #include <zephyr/shell/shell.h>
20 #include <zephyr/sys/crc.h>
21 
22 #include <lwm2m_engine.h>
23 #include <lwm2m_util.h>
24 
25 #define LWM2M_HELP_CMD "LwM2M commands"
26 #define LWM2M_HELP_SEND "send PATHS\nLwM2M SEND operation\n"
27 #define LWM2M_HELP_EXEC "exec PATH [PARAM]\nExecute a resource\n"
28 #define LWM2M_HELP_READ "read PATH [OPTIONS]\nRead value from LwM2M resource\n" \
29 	"-x \tRead value as hex stream (default)\n" \
30 	"-s \tRead value as string\n" \
31 	"-b \tRead value as bool (1/0)\n" \
32 	"-uX\tRead value as uintX_t\n" \
33 	"-sX\tRead value as intX_t\n" \
34 	"-f \tRead value as float\n" \
35 	"-t \tRead value as time_t\n" \
36 	"-crc32\tCalculate CRC32 of the content\n"
37 #define LWM2M_HELP_WRITE "write PATH [OPTIONS] VALUE\nWrite into LwM2M resource\n" \
38 	"-s \tWrite value as string (default)\n" \
39 	"-b \tWrite value as bool\n" \
40 	"-uX\tWrite value as uintX_t\n" \
41 	"-sX\tWrite value as intX_t\n" \
42 	"-f \tWrite value as float\n" \
43 	"-t \tWrite value as time_t\n"
44 #define LWM2M_HELP_CREATE "create PATH\nCreate object or resource instance\n"
45 #define LWM2M_HELP_DELETE "delete PATH\nDelete object or resource instance\n"
46 #define LWM2M_HELP_START "start EP_NAME [BOOTSTRAP FLAG]\n" \
47 	"Start the LwM2M RD (Registration / Discovery) Client\n" \
48 	"-b \tSet the bootstrap flag (default 0)\n"
49 #define LWM2M_HELP_STOP "stop [OPTIONS]\nStop the LwM2M RD (De-register) Client\n" \
50 	"-f \tForce close the connection\n"
51 #define LWM2M_HELP_UPDATE "Trigger Registration Update of the LwM2M RD Client\n"
52 #define LWM2M_HELP_PAUSE "LwM2M engine thread pause"
53 #define LWM2M_HELP_RESUME "LwM2M engine thread resume"
54 #define LWM2M_HELP_LOCK "Lock the LwM2M registry"
55 #define LWM2M_HELP_UNLOCK "Unlock the LwM2M registry"
56 #define LWM2M_HELP_OBSERV "List observations"
57 #define LWM2M_HELP_CACHE "cache PATH NUM\nEnable data cache for resource\n" \
58 	"PATH is LwM2M path\n" \
59 	"NUM how many elements to cache\n"
60 #define LWM2M_HELP_LS "ls [PATH]\nList objects, instances, resources\n"
61 
send_cb(enum lwm2m_send_status status)62 static void send_cb(enum lwm2m_send_status status)
63 {
64 	LOG_INF("SEND status: %d", status);
65 }
66 
cmd_send(const struct shell * sh,size_t argc,char ** argv)67 static int cmd_send(const struct shell *sh, size_t argc, char **argv)
68 {
69 	int ret = 0;
70 	struct lwm2m_ctx *ctx = lwm2m_rd_client_ctx();
71 	int path_cnt = argc - 1;
72 	struct lwm2m_obj_path lwm2m_path_list[CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE];
73 
74 	if (!ctx) {
75 		shell_error(sh, "no lwm2m context yet");
76 		return -ENOEXEC;
77 	}
78 
79 	if (argc < 2) {
80 		shell_error(sh, "no path(s)");
81 		shell_help(sh);
82 		return -EINVAL;
83 	}
84 
85 	if (path_cnt > CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE) {
86 		return -E2BIG;
87 	}
88 
89 	for (int i = 0; i < path_cnt; i++) {
90 		const char *p = argv[1 + i];
91 		/* translate path -> path_obj */
92 		ret = lwm2m_string_to_path(p, &lwm2m_path_list[i], '/');
93 		if (ret < 0) {
94 			return ret;
95 		}
96 	}
97 
98 	ret = lwm2m_send_cb(ctx, lwm2m_path_list, path_cnt, send_cb);
99 
100 	if (ret < 0) {
101 		shell_error(sh, "can't do send operation, request failed (%d)", ret);
102 		return -ENOEXEC;
103 	}
104 	return 0;
105 }
106 
cmd_exec(const struct shell * sh,size_t argc,char ** argv)107 static int cmd_exec(const struct shell *sh, size_t argc, char **argv)
108 {
109 	struct lwm2m_ctx *ctx = lwm2m_rd_client_ctx();
110 
111 	if (!ctx) {
112 		shell_error(sh, "no lwm2m context yet");
113 		return -ENOEXEC;
114 	}
115 
116 	const char *pathstr = argv[1];
117 	struct lwm2m_obj_path path;
118 	int ret = lwm2m_string_to_path(pathstr, &path, '/'); /* translate path -> path_obj */
119 
120 	if (ret < 0) {
121 		shell_error(sh, "Illegal path (PATH %s)", pathstr);
122 		return -EINVAL;
123 	}
124 
125 	struct lwm2m_engine_res *res = lwm2m_engine_get_res(&path);
126 
127 	if (res == NULL) {
128 		shell_error(sh, "Resource not found");
129 		return -EINVAL;
130 	}
131 
132 	if (!res->execute_cb) {
133 		shell_error(sh, "No execute callback!");
134 		return -EINVAL;
135 	}
136 
137 	/* 0: exec, 1:<path> 2:[<param>] */
138 	char *param = (argc == 3) ? argv[2] : NULL;
139 	size_t param_len = param ? strlen(param) + 1 : 0;
140 
141 	ret = res->execute_cb(path.obj_inst_id, param, param_len);
142 	if (ret < 0) {
143 		shell_error(sh, "returned (err %d)", ret);
144 		return -ENOEXEC;
145 	}
146 
147 	return 0;
148 }
149 
cmd_read(const struct shell * sh,size_t argc,char ** argv)150 static int cmd_read(const struct shell *sh, size_t argc, char **argv)
151 {
152 	struct lwm2m_ctx *ctx = lwm2m_rd_client_ctx();
153 
154 	if (!ctx) {
155 		shell_error(sh, "no lwm2m context yet");
156 		return -ENOEXEC;
157 	}
158 
159 	if (argc < 2) {
160 		shell_error(sh, "no arguments or path(s)");
161 		shell_help(sh);
162 		return -EINVAL;
163 	}
164 	const char *dtype = "-x"; /* default */
165 	const char *pathstr = argv[1];
166 	int ret = 0;
167 	struct lwm2m_obj_path path;
168 
169 	ret = lwm2m_string_to_path(pathstr, &path, '/');
170 	if (ret < 0) {
171 		return ret;
172 	}
173 
174 	if (argc > 2) { /* read + path + options(data type) */
175 		dtype = argv[2];
176 	}
177 	if (strcmp(dtype, "-x") == 0) {
178 		const char *buff;
179 		uint16_t buff_len = 0;
180 
181 		ret = lwm2m_get_res_buf(&path, (void **)&buff,
182 					NULL, &buff_len, NULL);
183 		if (ret != 0) {
184 			goto out;
185 		}
186 		shell_hexdump(sh, buff, buff_len);
187 	} else if (strcmp(dtype, "-crc32") == 0) {
188 		const char *buff;
189 		uint16_t buff_len = 0;
190 
191 		ret = lwm2m_get_res_buf(&path, (void **)&buff,
192 					NULL, &buff_len, NULL);
193 		if (ret != 0) {
194 			goto out;
195 		}
196 
197 		uint32_t crc = crc32_ieee(buff, buff_len);
198 
199 		shell_print(sh, "%u", crc);
200 	} else if (strcmp(dtype, "-s") == 0) {
201 		const char *buff;
202 		uint16_t buff_len = 0;
203 
204 		ret = lwm2m_get_res_buf(&path, (void **)&buff,
205 					NULL, &buff_len, NULL);
206 		if (ret != 0) {
207 			goto out;
208 		}
209 		shell_print(sh, "%.*s", buff_len, buff);
210 	} else if (strcmp(dtype, "-s8") == 0) {
211 		int8_t temp = 0;
212 
213 		ret = lwm2m_get_s8(&path, &temp);
214 		if (ret != 0) {
215 			goto out;
216 		}
217 		shell_print(sh, "%d", temp);
218 	} else if (strcmp(dtype, "-s16") == 0) {
219 		int16_t temp = 0;
220 
221 		ret = lwm2m_get_s16(&path, &temp);
222 		if (ret != 0) {
223 			goto out;
224 		}
225 		shell_print(sh, "%d", temp);
226 	} else if (strcmp(dtype, "-s32") == 0) {
227 		int32_t temp = 0;
228 
229 		ret = lwm2m_get_s32(&path, &temp);
230 		if (ret != 0) {
231 			goto out;
232 		}
233 		shell_print(sh, "%d", temp);
234 	} else if (strcmp(dtype, "-s64") == 0) {
235 		int64_t temp = 0;
236 
237 		ret = lwm2m_get_s64(&path, &temp);
238 		if (ret != 0) {
239 			goto out;
240 		}
241 		shell_print(sh, "%lld", temp);
242 	} else if (strcmp(dtype, "-u8") == 0) {
243 		uint8_t temp = 0;
244 
245 		ret = lwm2m_get_u8(&path, &temp);
246 		if (ret != 0) {
247 			goto out;
248 		}
249 		shell_print(sh, "%d", temp);
250 	} else if (strcmp(dtype, "-u16") == 0) {
251 		uint16_t temp = 0;
252 
253 		ret = lwm2m_get_u16(&path, &temp);
254 		if (ret != 0) {
255 			goto out;
256 		}
257 		shell_print(sh, "%d", temp);
258 	} else if (strcmp(dtype, "-u32") == 0) {
259 		uint32_t temp = 0;
260 
261 		ret = lwm2m_get_u32(&path, &temp);
262 		if (ret != 0) {
263 			goto out;
264 		}
265 		shell_print(sh, "%d", temp);
266 	} else if (strcmp(dtype, "-f") == 0) {
267 		double temp = 0;
268 
269 		ret = lwm2m_get_f64(&path, &temp);
270 		if (ret != 0) {
271 			goto out;
272 		}
273 		shell_print(sh, "%f", temp);
274 	} else if (strcmp(dtype, "-b") == 0) {
275 		bool temp;
276 
277 		ret = lwm2m_get_bool(&path, &temp);
278 		if (ret != 0) {
279 			goto out;
280 		}
281 		shell_print(sh, "%d", temp);
282 	} else if (strcmp(dtype, "-t") == 0) {
283 		time_t temp;
284 
285 		ret = lwm2m_get_time(&path, &temp);
286 		if (ret != 0) {
287 			goto out;
288 		}
289 		shell_print(sh, "%lld", temp);
290 	} else {
291 		shell_error(sh, "can't recognize data type %s", dtype);
292 		shell_help(sh);
293 		return -EINVAL;
294 	}
295 	return 0;
296 out:
297 	shell_error(sh, "can't do read operation, request failed (err %d)", ret);
298 	return -EINVAL;
299 }
300 
cmd_write(const struct shell * sh,size_t argc,char ** argv)301 static int cmd_write(const struct shell *sh, size_t argc, char **argv)
302 {
303 	struct lwm2m_ctx *ctx = lwm2m_rd_client_ctx();
304 
305 	if (!ctx) {
306 		shell_error(sh, "no lwm2m context yet");
307 		return -ENOEXEC;
308 	}
309 
310 	if (argc < 3) {
311 		shell_error(sh, "no arguments or path(s)");
312 		shell_help(sh);
313 		return -EINVAL;
314 	}
315 
316 	int ret = 0;
317 	const char *pathstr = argv[1];
318 	const char *dtype;
319 	char *value;
320 	struct lwm2m_obj_path path;
321 
322 	ret = lwm2m_string_to_path(pathstr, &path, '/');
323 	if (ret < 0) {
324 		return ret;
325 	}
326 
327 	if (argc == 4) { /* write path options value */
328 		dtype = argv[2];
329 		value = argv[3];
330 	} else { /* write path value */
331 		dtype = "-s";
332 		value = argv[2];
333 	}
334 
335 	if (strcmp(dtype, "-s") == 0) {
336 		ret = lwm2m_set_string(&path, value);
337 	} else if (strcmp(dtype, "-f") == 0) {
338 		double new = 0;
339 
340 		ret = lwm2m_atof(value, &new); /* Convert string -> float */
341 		if (ret == 0) {
342 			ret = lwm2m_set_f64(&path, new);
343 		}
344 	} else { /* All the types using stdlib funcs*/
345 		char *e;
346 
347 		if (strcmp(dtype, "-s8") == 0) {
348 			ret = lwm2m_set_s8(&path, strtol(value, &e, 10));
349 		} else if (strcmp(dtype, "-s16") == 0) {
350 			ret = lwm2m_set_s16(&path, strtol(value, &e, 10));
351 		} else if (strcmp(dtype, "-s32") == 0) {
352 			ret = lwm2m_set_s32(&path, strtol(value, &e, 10));
353 		} else if (strcmp(dtype, "-s64") == 0) {
354 			ret = lwm2m_set_s64(&path, strtoll(value, &e, 10));
355 		} else if (strcmp(dtype, "-u8") == 0) {
356 			ret = lwm2m_set_u8(&path, strtoul(value, &e, 10));
357 		} else if (strcmp(dtype, "-u16") == 0) {
358 			ret = lwm2m_set_u16(&path, strtoul(value, &e, 10));
359 		} else if (strcmp(dtype, "-u32") == 0) {
360 			ret = lwm2m_set_u32(&path, strtoul(value, &e, 10));
361 		} else if (strcmp(dtype, "-b") == 0) {
362 			ret = lwm2m_set_bool(&path, strtoul(value, &e, 10));
363 		} else if (strcmp(dtype, "-t") == 0) {
364 			ret = lwm2m_set_time(&path, strtoll(value, &e, 10));
365 		} else {
366 			shell_error(sh, "can't recognize data type %s",
367 				    dtype);
368 			shell_help(sh);
369 			return -EINVAL;
370 		}
371 		if (*e != '\0') {
372 			shell_error(sh, "Invalid number: %s", value);
373 			shell_help(sh);
374 			return -EINVAL;
375 		}
376 	}
377 
378 	if (ret < 0) {
379 		shell_error(
380 			sh,
381 			"can't do write operation, request failed (err %d)",
382 			ret);
383 		return -ENOEXEC;
384 	}
385 
386 	return 0;
387 }
388 
cmd_create_or_delete(const struct shell * sh,bool delete,size_t argc,char ** argv)389 static int cmd_create_or_delete(const struct shell *sh, bool delete, size_t argc, char **argv)
390 {
391 	struct lwm2m_obj_path path;
392 	int ret;
393 
394 	if (argc < 2) {
395 		shell_error(sh, "No object ID given");
396 		shell_help(sh);
397 		return -EINVAL;
398 	}
399 
400 	ret = lwm2m_string_to_path(argv[1], &path, '/');
401 	if (ret < 0) {
402 		shell_error(sh, "failed to read path (%d)", ret);
403 		return -ENOEXEC;
404 	}
405 
406 	if (delete) {
407 		switch (path.level) {
408 		case LWM2M_PATH_LEVEL_RESOURCE_INST:
409 			ret = lwm2m_delete_res_inst(&path);
410 			break;
411 		case LWM2M_PATH_LEVEL_OBJECT_INST:
412 			ret = lwm2m_delete_object_inst(&path);
413 			break;
414 		default:
415 			return -ENOEXEC;
416 		}
417 	} else {
418 		switch (path.level) {
419 		case LWM2M_PATH_LEVEL_RESOURCE_INST:
420 			ret = lwm2m_create_res_inst(&path);
421 			break;
422 		case LWM2M_PATH_LEVEL_OBJECT_INST:
423 			ret = lwm2m_create_object_inst(&path);
424 			break;
425 		default:
426 			return -ENOEXEC;
427 		}
428 	}
429 
430 	if (ret < 0) {
431 		shell_error(sh, "operation failed, %d", ret);
432 		return -ENOEXEC;
433 	}
434 
435 	return 0;
436 }
437 
cmd_create(const struct shell * sh,size_t argc,char ** argv)438 static int cmd_create(const struct shell *sh, size_t argc, char **argv)
439 {
440 	return cmd_create_or_delete(sh, false, argc, argv);
441 }
442 
cmd_delete(const struct shell * sh,size_t argc,char ** argv)443 static int cmd_delete(const struct shell *sh, size_t argc, char **argv)
444 {
445 	return cmd_create_or_delete(sh, true, argc, argv);
446 }
447 
cmd_start(const struct shell * sh,size_t argc,char ** argv)448 static int cmd_start(const struct shell *sh, size_t argc, char **argv)
449 {
450 	struct lwm2m_ctx *ctx = lwm2m_rd_client_ctx();
451 
452 	if (!ctx) {
453 		shell_error(sh, "no lwm2m context yet");
454 		return -ENOEXEC;
455 	}
456 	uint32_t bootstrap_flag = 0;
457 
458 	if (argc == 3) {
459 		shell_error(sh, "no specifier or value");
460 		shell_help(sh);
461 		return -EINVAL;
462 	} else if (argc == 4) {
463 		if (strcmp(argv[2], "-b") != 0) {
464 			shell_error(sh, "unknown specifier %s", argv[2]);
465 			shell_help(sh);
466 			return -EINVAL;
467 		}
468 
469 		char *e;
470 
471 		bootstrap_flag = strtol(argv[3], &e, 10);
472 		if (*e != '\0') {
473 			shell_error(sh, "Invalid number: %s", argv[3]);
474 			shell_help(sh);
475 			return -EINVAL;
476 		}
477 	}
478 	int ret = lwm2m_rd_client_start(ctx, argv[1], bootstrap_flag,
479 					ctx->event_cb, ctx->observe_cb);
480 	if (ret < 0) {
481 		shell_error(
482 			sh,
483 			"can't do start operation, request failed (err %d)",
484 			ret);
485 		return -ENOEXEC;
486 	}
487 	return 0;
488 }
489 
cmd_stop(const struct shell * sh,size_t argc,char ** argv)490 static int cmd_stop(const struct shell *sh, size_t argc, char **argv)
491 {
492 	struct lwm2m_ctx *ctx = lwm2m_rd_client_ctx();
493 
494 	if (!ctx) {
495 		shell_error(sh, "no lwm2m context yet");
496 		return -ENOEXEC;
497 	}
498 	bool forcefully = true;
499 
500 	if (argc == 2) {
501 		if (strcmp(argv[1], "-f") != 0) {
502 			shell_error(sh, "can't recognize specifier %s",
503 				    argv[1]);
504 			shell_help(sh);
505 			return -EINVAL;
506 		}
507 		forcefully = false;
508 	}
509 	int ret = lwm2m_rd_client_stop(ctx, ctx->event_cb, forcefully);
510 
511 	if (ret < 0) {
512 		shell_error(
513 			sh,
514 			"can't do stop operation, request failed (err %d)",
515 			ret);
516 		return -ENOEXEC;
517 	}
518 	return 0;
519 }
520 
cmd_update(const struct shell * sh,size_t argc,char ** argv)521 static int cmd_update(const struct shell *sh, size_t argc, char **argv)
522 {
523 	ARG_UNUSED(argc);
524 	ARG_UNUSED(argv);
525 	struct lwm2m_ctx *ctx = lwm2m_rd_client_ctx();
526 
527 	if (!ctx) {
528 		shell_error(sh, "no lwm2m context yet");
529 		return -ENOEXEC;
530 	}
531 	lwm2m_rd_client_update();
532 	return 0;
533 }
534 
cmd_pause(const struct shell * sh,size_t argc,char ** argv)535 static int cmd_pause(const struct shell *sh, size_t argc, char **argv)
536 {
537 	ARG_UNUSED(sh);
538 	ARG_UNUSED(argc);
539 	ARG_UNUSED(argv);
540 
541 	return lwm2m_engine_pause();
542 }
543 
cmd_resume(const struct shell * sh,size_t argc,char ** argv)544 static int cmd_resume(const struct shell *sh, size_t argc, char **argv)
545 {
546 	ARG_UNUSED(sh);
547 	ARG_UNUSED(argc);
548 	ARG_UNUSED(argv);
549 
550 	return lwm2m_engine_resume();
551 }
552 
cmd_lock(const struct shell * sh,size_t argc,char ** argv)553 static int cmd_lock(const struct shell *sh, size_t argc, char **argv)
554 {
555 	ARG_UNUSED(sh);
556 	ARG_UNUSED(argc);
557 	ARG_UNUSED(argv);
558 
559 	lwm2m_registry_lock();
560 	return 0;
561 }
562 
cmd_unlock(const struct shell * sh,size_t argc,char ** argv)563 static int cmd_unlock(const struct shell *sh, size_t argc, char **argv)
564 {
565 	ARG_UNUSED(sh);
566 	ARG_UNUSED(argc);
567 	ARG_UNUSED(argv);
568 
569 	lwm2m_registry_unlock();
570 	return 0;
571 }
572 
cmd_cache(const struct shell * sh,size_t argc,char ** argv)573 static int cmd_cache(const struct shell *sh, size_t argc, char **argv)
574 {
575 #if (K_HEAP_MEM_POOL_SIZE > 0)
576 	int rc;
577 	int elems;
578 	struct lwm2m_time_series_elem *cache;
579 	struct lwm2m_obj_path obj_path;
580 
581 	if (argc != 3) {
582 		shell_error(sh, "wrong parameters");
583 		return -EINVAL;
584 	}
585 
586 	/* translate path -> path_obj */
587 	rc = lwm2m_string_to_path(argv[1], &obj_path, '/');
588 	if (rc < 0) {
589 		return rc;
590 	}
591 
592 	if (obj_path.level < 3) {
593 		shell_error(sh, "Path string not correct");
594 		return -EINVAL;
595 	}
596 
597 	if (lwm2m_cache_entry_get_by_object(&obj_path)) {
598 		shell_error(sh, "Cache already enabled for %s", argv[1]);
599 		return -ENOEXEC;
600 	}
601 
602 	elems = atoi(argv[2]);
603 	if (elems < 1) {
604 		shell_error(sh, "Size must be 1 or more (given %d)", elems);
605 		return -EINVAL;
606 	}
607 
608 	cache = k_malloc(sizeof(struct lwm2m_time_series_elem) * elems);
609 	if (!cache) {
610 		shell_error(sh, "Out of memory");
611 		return -ENOEXEC;
612 	}
613 
614 	rc = lwm2m_enable_cache(&obj_path, cache, elems);
615 	if (rc) {
616 		shell_error(sh, "lwm2m_enable_cache(%u/%u/%u/%u, %p, %d) returned %d",
617 			    obj_path.obj_id, obj_path.obj_inst_id, obj_path.res_id,
618 			    obj_path.res_inst_id, cache, elems, rc);
619 		k_free(cache);
620 		return -ENOEXEC;
621 	}
622 
623 	return 0;
624 #else
625 	shell_error(sh, "No heap configured");
626 	return -ENOEXEC;
627 #endif
628 }
629 
shell_print_attr(const struct shell * sh,void * ref)630 static void shell_print_attr(const struct shell *sh, void *ref)
631 {
632 	struct lwm2m_attr *attr = NULL;
633 	bool found;
634 
635 	for (uint8_t type = 0; type < NR_LWM2M_ATTR; type++) {
636 		found = false;
637 		while ((attr = lwm2m_engine_get_next_attr(ref, attr)) != NULL) {
638 			if (attr->type == type) {
639 				found = true;
640 				break;
641 			}
642 		}
643 		if (found) {
644 			switch (type) {
645 			case LWM2M_ATTR_PMIN:
646 				/* fall through */
647 			case LWM2M_ATTR_PMAX:
648 				shell_fprintf(sh, SHELL_NORMAL, "%10u", attr->int_val);
649 				break;
650 			case LWM2M_ATTR_GT:
651 				/* fall through */
652 			case LWM2M_ATTR_LT:
653 				/* fall through */
654 			case LWM2M_ATTR_STEP:
655 				shell_fprintf(sh, SHELL_NORMAL, "%10f", attr->float_val);
656 				break;
657 			}
658 		} else {
659 			shell_fprintf(sh, SHELL_NORMAL, "%10s", "");
660 		}
661 	}
662 }
663 
cmd_observations(const struct shell * sh,size_t argc,char ** argv)664 static int cmd_observations(const struct shell *sh, size_t argc, char **argv)
665 {
666 	char buf[LWM2M_MAX_PATH_STR_SIZE];
667 	struct lwm2m_obj_path_list *o_p;
668 	struct observe_node *obs;
669 	uint32_t i = 0, path_i;
670 	struct lwm2m_ctx *ctx;
671 	void *ref;
672 	int ret;
673 
674 	ctx = lwm2m_rd_client_ctx();
675 	if (ctx == NULL) {
676 		shell_error(sh, "no lwm2m context yet\n");
677 		return -ENOEXEC;
678 	}
679 
680 	ARG_UNUSED(argc);
681 	ARG_UNUSED(argv);
682 
683 	shell_fprintf(sh, SHELL_INFO, " # %10s %18s", "composite", "path");
684 	for (i = 0; i < NR_LWM2M_ATTR; i++) {
685 		shell_fprintf(sh, SHELL_INFO, "%10s", lwm2m_attr_to_str(i));
686 	}
687 	shell_print(sh, "");
688 
689 	lwm2m_registry_lock();
690 	i = 0;
691 	SYS_SLIST_FOR_EACH_CONTAINER(&ctx->observer, obs, node) {
692 		shell_fprintf(sh, SHELL_NORMAL, "%2u %10c ", i, obs->composite ? 'y' : 'n');
693 		path_i = 0;
694 		SYS_SLIST_FOR_EACH_CONTAINER(&obs->path_list, o_p, node) {
695 			if (path_i > 0) {
696 				shell_fprintf(sh, SHELL_NORMAL, "%14s", "");
697 			}
698 			shell_fprintf(sh, SHELL_NORMAL, "%-18s",
699 				      lwm2m_path_log_buf(buf, &o_p->path));
700 			ret = lwm2m_get_path_reference_ptr(NULL, &o_p->path, &ref);
701 			if (ret < 0) {
702 				continue;
703 			}
704 			shell_print_attr(sh, ref);
705 			path_i++;
706 			shell_print(sh, "");
707 		}
708 		i++;
709 
710 	}
711 	lwm2m_registry_unlock();
712 
713 	return 0;
714 }
715 
print_object_instance(const struct shell * sh,struct lwm2m_engine_obj_inst * oi)716 static int print_object_instance(const struct shell *sh, struct lwm2m_engine_obj_inst *oi)
717 {
718 	struct lwm2m_engine_obj *obj;
719 
720 	if (!oi) {
721 		return -ENOEXEC;
722 	}
723 
724 	obj = oi->obj;
725 
726 	for (int i = 0; i < oi->resource_count; i++) {
727 		struct lwm2m_engine_res *re = &oi->resources[i];
728 
729 		for (int j = 0; j < re->res_inst_count; j++) {
730 			struct lwm2m_engine_res_inst *ri = &re->res_instances[j];
731 
732 			if (ri->data_ptr && ri->data_len > 0 &&
733 			    ri->res_inst_id != RES_INSTANCE_NOT_CREATED) {
734 				struct lwm2m_engine_obj_field *field =
735 					lwm2m_get_engine_obj_field(obj, re->res_id);
736 				char path[LWM2M_MAX_PATH_STR_SIZE];
737 
738 				if (field == NULL) {
739 					continue;
740 				}
741 
742 				if (re->multi_res_inst) {
743 					snprintf(path, sizeof(path), "%hu/%hu/%hu/%hu", obj->obj_id,
744 						 oi->obj_inst_id, re->res_id, ri->res_inst_id);
745 				} else {
746 					snprintf(path, sizeof(path), "%hu/%hu/%hu", obj->obj_id,
747 						 oi->obj_inst_id, re->res_id);
748 				}
749 				switch (field->data_type) {
750 				case LWM2M_RES_TYPE_STRING:
751 					shell_print(sh, "%s : %s", path, (char *)ri->data_ptr);
752 					break;
753 				case LWM2M_RES_TYPE_U8:
754 				case LWM2M_RES_TYPE_S8:
755 				case LWM2M_RES_TYPE_BOOL:
756 					shell_print(sh, "%s : %u", path, *(uint8_t *)ri->data_ptr);
757 					break;
758 				case LWM2M_RES_TYPE_U16:
759 				case LWM2M_RES_TYPE_S16:
760 					shell_print(sh, "%s : %u", path, *(uint16_t *)ri->data_ptr);
761 					break;
762 				case LWM2M_RES_TYPE_U32:
763 				case LWM2M_RES_TYPE_S32:
764 					shell_print(sh, "%s : %u", path, *(uint32_t *)ri->data_ptr);
765 					break;
766 				case LWM2M_RES_TYPE_S64:
767 				case LWM2M_RES_TYPE_TIME:
768 					shell_print(sh, "%s : %lld", path,
769 						    *(int64_t *)ri->data_ptr);
770 					break;
771 				case LWM2M_RES_TYPE_FLOAT:
772 					shell_print(sh, "%s : %lf", path, *(double *)ri->data_ptr);
773 					break;
774 				case LWM2M_RES_TYPE_OPAQUE:
775 					shell_print(sh, "%s : OPAQUE(%hu/%hu)", path, ri->data_len,
776 						    ri->max_data_len);
777 					break;
778 				}
779 			}
780 		}
781 	}
782 	return 0;
783 }
784 
print_object(const struct shell * sh,struct lwm2m_engine_obj * obj)785 static int print_object(const struct shell *sh, struct lwm2m_engine_obj *obj)
786 {
787 	if (!obj) {
788 		return -ENOEXEC;
789 	}
790 	struct lwm2m_engine_obj_inst *oi = next_engine_obj_inst(obj->obj_id, -1);
791 
792 	for (int i = 0; i < obj->instance_count; i++) {
793 		print_object_instance(sh, oi);
794 		oi = next_engine_obj_inst(obj->obj_id, oi->obj_inst_id);
795 	}
796 	return 0;
797 }
798 
print_all_objs(const struct shell * sh)799 static int print_all_objs(const struct shell *sh)
800 {
801 	struct lwm2m_engine_obj *obj;
802 
803 	SYS_SLIST_FOR_EACH_CONTAINER(lwm2m_engine_obj_list(), obj, node) {
804 		print_object(sh, obj);
805 	}
806 	return 0;
807 }
808 
cmd_ls(const struct shell * sh,size_t argc,char ** argv)809 static int cmd_ls(const struct shell *sh, size_t argc, char **argv)
810 {
811 	struct lwm2m_obj_path path;
812 	int ret;
813 
814 	if (argc < 2) {
815 		return print_all_objs(sh);
816 	}
817 
818 	ret = lwm2m_string_to_path(argv[1], &path, '/');
819 	if (ret < 0) {
820 		return -ENOEXEC;
821 	}
822 
823 	if (path.level == LWM2M_PATH_LEVEL_NONE) {
824 		return print_all_objs(sh);
825 	} else if (path.level == LWM2M_PATH_LEVEL_OBJECT) {
826 		struct lwm2m_engine_obj *obj = lwm2m_engine_get_obj(&path);
827 
828 		return print_object(sh, obj);
829 	} else if (path.level == LWM2M_PATH_LEVEL_OBJECT_INST) {
830 		struct lwm2m_engine_obj_inst *oi = lwm2m_engine_get_obj_inst(&path);
831 
832 		return print_object_instance(sh, oi);
833 	} else if (path.level == LWM2M_PATH_LEVEL_RESOURCE) {
834 		return cmd_read(sh, argc, argv);
835 	}
836 	return -ENOEXEC;
837 }
838 
839 SHELL_STATIC_SUBCMD_SET_CREATE(
840 	sub_lwm2m,
841 	SHELL_COND_CMD_ARG(CONFIG_LWM2M_VERSION_1_1, send, NULL,
842 			   LWM2M_HELP_SEND, cmd_send, 1, 9),
843 	SHELL_CMD_ARG(exec, NULL, LWM2M_HELP_EXEC, cmd_exec, 2, 1),
844 	SHELL_CMD_ARG(read, NULL, LWM2M_HELP_READ, cmd_read, 2, 1),
845 	SHELL_CMD_ARG(write, NULL, LWM2M_HELP_WRITE, cmd_write, 3, 1),
846 	SHELL_CMD_ARG(create, NULL, LWM2M_HELP_CREATE, cmd_create, 2, 0),
847 	SHELL_CMD_ARG(delete, NULL, LWM2M_HELP_DELETE, cmd_delete, 2, 0),
848 	SHELL_CMD_ARG(cache, NULL, LWM2M_HELP_CACHE, cmd_cache, 3, 0),
849 	SHELL_CMD_ARG(start, NULL, LWM2M_HELP_START, cmd_start, 2, 2),
850 	SHELL_CMD_ARG(stop, NULL, LWM2M_HELP_STOP, cmd_stop, 1, 1),
851 	SHELL_CMD_ARG(update, NULL, LWM2M_HELP_UPDATE, cmd_update, 1, 0),
852 	SHELL_CMD_ARG(pause, NULL, LWM2M_HELP_PAUSE, cmd_pause, 1, 0),
853 	SHELL_CMD_ARG(resume, NULL, LWM2M_HELP_RESUME, cmd_resume, 1, 0),
854 	SHELL_CMD_ARG(lock, NULL, LWM2M_HELP_LOCK, cmd_lock, 1, 0),
855 	SHELL_CMD_ARG(unlock, NULL, LWM2M_HELP_UNLOCK, cmd_unlock, 1, 0),
856 	SHELL_CMD_ARG(obs, NULL, LWM2M_HELP_OBSERV, cmd_observations, 1, 0),
857 	SHELL_CMD_ARG(ls, NULL, LWM2M_HELP_LS, cmd_ls, 1, 1),
858 	SHELL_SUBCMD_SET_END);
859 SHELL_COND_CMD_ARG_REGISTER(CONFIG_LWM2M_SHELL, lwm2m, &sub_lwm2m,
860 			    LWM2M_HELP_CMD, NULL, 1, 0);
861