1 /*
2 * Copyright (c) 2023 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/sys/util.h>
9 #include <zephyr/settings/settings.h>
10 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
11 #include <zephyr/mgmt/mcumgr/smp/smp.h>
12 #include <zephyr/mgmt/mcumgr/mgmt/handlers.h>
13 #include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
14 #include <zephyr/mgmt/mcumgr/grp/settings_mgmt/settings_mgmt.h>
15 #include <mgmt/mcumgr/util/zcbor_bulk.h>
16 #include <assert.h>
17 #include <limits.h>
18 #include <string.h>
19 #include <stdlib.h>
20
21 #include <zcbor_common.h>
22 #include <zcbor_decode.h>
23 #include <zcbor_encode.h>
24
25 #define LOG_LEVEL CONFIG_MCUMGR_LOG_LEVEL
26 #include <zephyr/logging/log.h>
27 LOG_MODULE_REGISTER(settings_mgmt);
28
29 /**
30 * Command handler: settings read
31 */
settings_mgmt_read(struct smp_streamer * ctxt)32 static int settings_mgmt_read(struct smp_streamer *ctxt)
33 {
34 int rc;
35 zcbor_state_t *zse = ctxt->writer->zs;
36 zcbor_state_t *zsd = ctxt->reader->zs;
37 bool ok;
38 size_t decoded;
39 struct zcbor_string key = { 0 };
40 uint32_t max_size = CONFIG_MCUMGR_GRP_SETTINGS_VALUE_LEN;
41 bool limited_size = false;
42
43 #ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
44 char *key_name = NULL;
45 uint8_t *data = NULL;
46 #else
47 char key_name[CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN] = { 0 };
48 uint8_t data[CONFIG_MCUMGR_GRP_SETTINGS_VALUE_LEN] = { 0 };
49 #endif
50
51 struct zcbor_map_decode_key_val settings_read_decode[] = {
52 ZCBOR_MAP_DECODE_KEY_DECODER("name", zcbor_tstr_decode, &key),
53 ZCBOR_MAP_DECODE_KEY_DECODER("max_size", zcbor_uint32_decode, &max_size),
54 };
55
56 ok = zcbor_map_decode_bulk(zsd, settings_read_decode, ARRAY_SIZE(settings_read_decode),
57 &decoded) == 0;
58
59 if (!ok || key.len == 0) {
60 return MGMT_ERR_EINVAL;
61 }
62
63 /* Check if the length of the user supplied key is too large for the configuration
64 * to allow and return an error if so
65 */
66 if (key.len >= CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN) {
67 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SETTINGS,
68 SETTINGS_MGMT_ERR_KEY_TOO_LONG);
69 goto end;
70 }
71
72 if (max_size > CONFIG_MCUMGR_GRP_SETTINGS_VALUE_LEN) {
73 max_size = CONFIG_MCUMGR_GRP_SETTINGS_VALUE_LEN;
74 limited_size = true;
75 }
76
77 #ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
78 key_name = (char *)malloc(key.len + 1);
79 data = (uint8_t *)malloc(max_size);
80
81 if (data == NULL || key_name == NULL) {
82 if (key_name != NULL) {
83 free(key_name);
84 }
85
86 return MGMT_ERR_ENOMEM;
87 }
88 #endif
89
90 memcpy(key_name, key.value, key.len);
91 key_name[key.len] = 0;
92
93 if (IS_ENABLED(CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)) {
94 /* Send request to application to check if access should be allowed or not */
95 struct settings_mgmt_access settings_access_data = {
96 .access = SETTINGS_ACCESS_READ,
97 .name = key_name,
98 };
99
100 enum mgmt_cb_return status;
101 int32_t ret_rc;
102 uint16_t ret_group;
103
104 status = mgmt_callback_notify(MGMT_EVT_OP_SETTINGS_MGMT_ACCESS,
105 &settings_access_data, sizeof(settings_access_data),
106 &ret_rc, &ret_group);
107
108 if (status != MGMT_CB_OK) {
109 if (status == MGMT_CB_ERROR_RC) {
110 return ret_rc;
111 }
112
113 ok = smp_add_cmd_err(zse, ret_group, (uint16_t)ret_rc);
114 goto end;
115 }
116 }
117
118 /* Read the settings key */
119 rc = settings_runtime_get(key_name, data, max_size);
120
121 if (rc < 0) {
122 if (rc == -EINVAL) {
123 rc = SETTINGS_MGMT_ERR_ROOT_KEY_NOT_FOUND;
124 } else if (rc == -ENOENT) {
125 rc = SETTINGS_MGMT_ERR_KEY_NOT_FOUND;
126 } else if (rc == -ENOTSUP) {
127 rc = SETTINGS_MGMT_ERR_READ_NOT_SUPPORTED;
128 } else {
129 rc = SETTINGS_MGMT_ERR_UNKNOWN;
130 }
131
132 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SETTINGS, (uint16_t)rc);
133 goto end;
134 }
135
136 ok = zcbor_tstr_put_lit(zse, "val") &&
137 zcbor_bstr_encode_ptr(zse, data, rc);
138
139 if (ok && limited_size) {
140 ok = zcbor_tstr_put_lit(zse, "max_size") &&
141 zcbor_uint32_put(zse, CONFIG_MCUMGR_GRP_SETTINGS_VALUE_LEN);
142 }
143
144 end:
145 #ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
146 free(key_name);
147 free(data);
148 #endif
149
150 return MGMT_RETURN_CHECK(ok);
151 }
152
153 /**
154 * Command handler: settings write
155 */
settings_mgmt_write(struct smp_streamer * ctxt)156 static int settings_mgmt_write(struct smp_streamer *ctxt)
157 {
158 int rc;
159 zcbor_state_t *zse = ctxt->writer->zs;
160 zcbor_state_t *zsd = ctxt->reader->zs;
161 bool ok;
162 struct zcbor_string data = { 0 };
163 size_t decoded;
164 struct zcbor_string key = { 0 };
165
166 #ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
167 char *key_name = NULL;
168 #else
169 char key_name[CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN];
170 #endif
171
172 struct zcbor_map_decode_key_val settings_write_decode[] = {
173 ZCBOR_MAP_DECODE_KEY_DECODER("name", zcbor_tstr_decode, &key),
174 ZCBOR_MAP_DECODE_KEY_DECODER("val", zcbor_bstr_decode, &data),
175 };
176
177 ok = zcbor_map_decode_bulk(zsd, settings_write_decode, ARRAY_SIZE(settings_write_decode),
178 &decoded) == 0;
179
180 if (!ok || key.len == 0) {
181 return MGMT_ERR_EINVAL;
182 }
183
184 /* Check if the length of the user supplied key is too large for the configuration
185 * to allow and return an error if so
186 */
187 if (key.len >= CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN) {
188 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SETTINGS,
189 SETTINGS_MGMT_ERR_KEY_TOO_LONG);
190 goto end;
191 }
192
193 #ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
194 key_name = (char *)malloc(key.len + 1);
195
196 if (key_name == NULL) {
197 return MGMT_ERR_ENOMEM;
198 }
199 #endif
200
201 memcpy(key_name, key.value, key.len);
202 key_name[key.len] = 0;
203
204 if (IS_ENABLED(CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)) {
205 /* Send request to application to check if access should be allowed or not */
206 struct settings_mgmt_access settings_access_data = {
207 .access = SETTINGS_ACCESS_WRITE,
208 .name = key_name,
209 .val = data.value,
210 .val_length = &data.len,
211 };
212
213 enum mgmt_cb_return status;
214 int32_t ret_rc;
215 uint16_t ret_group;
216
217 status = mgmt_callback_notify(MGMT_EVT_OP_SETTINGS_MGMT_ACCESS,
218 &settings_access_data, sizeof(settings_access_data),
219 &ret_rc, &ret_group);
220
221 if (status != MGMT_CB_OK) {
222 if (status == MGMT_CB_ERROR_RC) {
223 return ret_rc;
224 }
225
226 ok = smp_add_cmd_err(zse, ret_group, (uint16_t)ret_rc);
227 goto end;
228 }
229 }
230
231 /* Update the settings data */
232 rc = settings_runtime_set(key_name, data.value, data.len);
233
234 if (rc < 0) {
235 if (rc == -EINVAL) {
236 rc = SETTINGS_MGMT_ERR_ROOT_KEY_NOT_FOUND;
237 } else if (rc == -ENOENT) {
238 rc = SETTINGS_MGMT_ERR_KEY_NOT_FOUND;
239 } else if (rc == -ENOTSUP) {
240 rc = SETTINGS_MGMT_ERR_WRITE_NOT_SUPPORTED;
241 } else {
242 rc = SETTINGS_MGMT_ERR_UNKNOWN;
243 }
244
245 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SETTINGS, (uint16_t)rc);
246 }
247
248 end:
249 #ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
250 free(key_name);
251 #endif
252
253 return MGMT_RETURN_CHECK(ok);
254 }
255
256 /**
257 * Command handler: settings delete
258 */
settings_mgmt_delete(struct smp_streamer * ctxt)259 static int settings_mgmt_delete(struct smp_streamer *ctxt)
260 {
261 zcbor_state_t *zse = ctxt->writer->zs;
262 zcbor_state_t *zsd = ctxt->reader->zs;
263 int rc;
264 bool ok;
265 size_t decoded;
266 struct zcbor_string key = { 0 };
267
268 #ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
269 char *key_name = NULL;
270 #else
271 char key_name[CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN];
272 #endif
273
274 struct zcbor_map_decode_key_val settings_delete_decode[] = {
275 ZCBOR_MAP_DECODE_KEY_DECODER("name", zcbor_tstr_decode, &key),
276 };
277
278 ok = zcbor_map_decode_bulk(zsd, settings_delete_decode, ARRAY_SIZE(settings_delete_decode),
279 &decoded) == 0;
280
281 if (!ok || key.len == 0) {
282 return MGMT_ERR_EINVAL;
283 }
284
285 /* Check if the length of the user supplied key is too large for the configuration
286 * to allow and return an error if so
287 */
288 if (key.len >= CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN) {
289 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SETTINGS,
290 SETTINGS_MGMT_ERR_KEY_TOO_LONG);
291 goto end;
292 }
293
294 #ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
295 key_name = (char *)malloc(key.len + 1);
296
297 if (key_name == NULL) {
298 return MGMT_ERR_ENOMEM;
299 }
300 #endif
301
302 memcpy(key_name, key.value, key.len);
303 key_name[key.len] = 0;
304
305 if (IS_ENABLED(CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)) {
306 /* Send request to application to check if access should be allowed or not */
307 struct settings_mgmt_access settings_access_data = {
308 .access = SETTINGS_ACCESS_DELETE,
309 .name = key_name,
310 };
311
312 enum mgmt_cb_return status;
313 int32_t ret_rc;
314 uint16_t ret_group;
315
316 status = mgmt_callback_notify(MGMT_EVT_OP_SETTINGS_MGMT_ACCESS,
317 &settings_access_data, sizeof(settings_access_data),
318 &ret_rc, &ret_group);
319
320 if (status != MGMT_CB_OK) {
321 if (status == MGMT_CB_ERROR_RC) {
322 return ret_rc;
323 }
324
325 ok = smp_add_cmd_err(zse, ret_group, (uint16_t)ret_rc);
326 goto end;
327 }
328 }
329
330 /* Delete requested key from settings */
331 rc = settings_delete(key_name);
332
333 #ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
334 free(key_name);
335 #endif
336
337 if (rc < 0) {
338 if (rc == -EINVAL) {
339 rc = SETTINGS_MGMT_ERR_ROOT_KEY_NOT_FOUND;
340 } else if (rc == -ENOENT) {
341 rc = SETTINGS_MGMT_ERR_KEY_NOT_FOUND;
342 } else if (rc == -ENOTSUP) {
343 rc = SETTINGS_MGMT_ERR_DELETE_NOT_SUPPORTED;
344 } else {
345 rc = SETTINGS_MGMT_ERR_UNKNOWN;
346 }
347
348 ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SETTINGS, (uint16_t)rc);
349 }
350
351 end:
352 return MGMT_RETURN_CHECK(ok);
353 }
354
355 /**
356 * Command handler: settings commit
357 */
settings_mgmt_commit(struct smp_streamer * ctxt)358 static int settings_mgmt_commit(struct smp_streamer *ctxt)
359 {
360 bool ok = true;
361
362 if (IS_ENABLED(CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)) {
363 /* Send request to application to check if access should be allowed or not */
364 struct settings_mgmt_access settings_access_data = {
365 .access = SETTINGS_ACCESS_COMMIT,
366 };
367
368 zcbor_state_t *zse = ctxt->writer->zs;
369 enum mgmt_cb_return status;
370 int32_t ret_rc;
371 uint16_t ret_group;
372
373 status = mgmt_callback_notify(MGMT_EVT_OP_SETTINGS_MGMT_ACCESS,
374 &settings_access_data, sizeof(settings_access_data),
375 &ret_rc, &ret_group);
376
377 if (status != MGMT_CB_OK) {
378 if (status == MGMT_CB_ERROR_RC) {
379 return ret_rc;
380 }
381
382 ok = smp_add_cmd_err(zse, ret_group, (uint16_t)ret_rc);
383 goto end;
384 }
385 }
386
387 settings_commit();
388
389 end:
390 return MGMT_RETURN_CHECK(ok);
391 }
392
393 /**
394 * Command handler: settings load
395 */
settings_mgmt_load(struct smp_streamer * ctxt)396 static int settings_mgmt_load(struct smp_streamer *ctxt)
397 {
398 bool ok = true;
399
400 if (IS_ENABLED(CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)) {
401 /* Send request to application to check if access should be allowed or not */
402 struct settings_mgmt_access settings_access_data = {
403 .access = SETTINGS_ACCESS_LOAD,
404 };
405
406 zcbor_state_t *zse = ctxt->writer->zs;
407 enum mgmt_cb_return status;
408 int32_t ret_rc;
409 uint16_t ret_group;
410
411 status = mgmt_callback_notify(MGMT_EVT_OP_SETTINGS_MGMT_ACCESS,
412 &settings_access_data, sizeof(settings_access_data),
413 &ret_rc, &ret_group);
414
415 if (status != MGMT_CB_OK) {
416 if (status == MGMT_CB_ERROR_RC) {
417 return ret_rc;
418 }
419
420 ok = smp_add_cmd_err(zse, ret_group, (uint16_t)ret_rc);
421 goto end;
422 }
423 }
424
425 settings_load();
426
427 end:
428 return MGMT_RETURN_CHECK(ok);
429 }
430
431 /**
432 * Command handler: settings save
433 */
settings_mgmt_save(struct smp_streamer * ctxt)434 static int settings_mgmt_save(struct smp_streamer *ctxt)
435 {
436 bool ok = true;
437
438 if (IS_ENABLED(CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)) {
439 /* Send request to application to check if access should be allowed or not */
440 struct settings_mgmt_access settings_access_data = {
441 .access = SETTINGS_ACCESS_SAVE,
442 };
443
444 zcbor_state_t *zse = ctxt->writer->zs;
445 enum mgmt_cb_return status;
446 int32_t ret_rc;
447 uint16_t ret_group;
448
449 status = mgmt_callback_notify(MGMT_EVT_OP_SETTINGS_MGMT_ACCESS,
450 &settings_access_data, sizeof(settings_access_data),
451 &ret_rc, &ret_group);
452
453 if (status != MGMT_CB_OK) {
454 if (status == MGMT_CB_ERROR_RC) {
455 return ret_rc;
456 }
457
458 ok = smp_add_cmd_err(zse, ret_group, (uint16_t)ret_rc);
459 goto end;
460 }
461 }
462
463 settings_save();
464
465 end:
466 return MGMT_RETURN_CHECK(ok);
467 }
468
469 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
settings_mgmt_translate_error_code(uint16_t ret)470 static int settings_mgmt_translate_error_code(uint16_t ret)
471 {
472 int rc;
473
474 switch (ret) {
475 case SETTINGS_MGMT_ERR_KEY_TOO_LONG:
476 rc = MGMT_ERR_EINVAL;
477 break;
478
479 case SETTINGS_MGMT_ERR_KEY_NOT_FOUND:
480 case SETTINGS_MGMT_ERR_READ_NOT_SUPPORTED:
481 rc = MGMT_ERR_ENOENT;
482 break;
483
484 case SETTINGS_MGMT_ERR_UNKNOWN:
485 default:
486 rc = MGMT_ERR_EUNKNOWN;
487 }
488
489 return rc;
490 }
491 #endif
492
493 static const struct mgmt_handler settings_mgmt_handlers[] = {
494 [SETTINGS_MGMT_ID_READ_WRITE] = {
495 .mh_read = settings_mgmt_read,
496 .mh_write = settings_mgmt_write,
497 },
498 [SETTINGS_MGMT_ID_DELETE] = {
499 .mh_read = NULL,
500 .mh_write = settings_mgmt_delete,
501 },
502 [SETTINGS_MGMT_ID_COMMIT] = {
503 .mh_read = NULL,
504 .mh_write = settings_mgmt_commit,
505 },
506 [SETTINGS_MGMT_ID_LOAD_SAVE] = {
507 .mh_read = settings_mgmt_load,
508 .mh_write = settings_mgmt_save,
509 },
510 };
511
512 static struct mgmt_group settings_mgmt_group = {
513 .mg_handlers = settings_mgmt_handlers,
514 .mg_handlers_count = ARRAY_SIZE(settings_mgmt_handlers),
515 .mg_group_id = MGMT_GROUP_ID_SETTINGS,
516 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
517 .mg_translate_error = settings_mgmt_translate_error_code,
518 #endif
519 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS_NAME
520 .mg_group_name = "settings mgmt",
521 #endif
522 };
523
settings_mgmt_register_group(void)524 static void settings_mgmt_register_group(void)
525 {
526 mgmt_register_group(&settings_mgmt_group);
527 }
528
529 MCUMGR_HANDLER_DEFINE(settings_mgmt, settings_mgmt_register_group);
530