1 /*
2 * Copyright (c) 2021 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/settings/settings.h>
8 #include <zephyr/shell/shell.h>
9 #include <zephyr/sys/util.h>
10 #include <zephyr/toolchain.h>
11
12 #include <ctype.h>
13 #include <stdint.h>
14 #include <stddef.h>
15
16 struct settings_list_callback_params {
17 const struct shell *shell_ptr;
18 const char *subtree;
19 };
20
settings_list_callback(const char * key,size_t len,settings_read_cb read_cb,void * cb_arg,void * param)21 static int settings_list_callback(const char *key,
22 size_t len,
23 settings_read_cb read_cb,
24 void *cb_arg,
25 void *param)
26 {
27 ARG_UNUSED(len);
28 ARG_UNUSED(read_cb);
29 ARG_UNUSED(cb_arg);
30
31 struct settings_list_callback_params *params = param;
32
33 if (params->subtree != NULL) {
34 shell_print(params->shell_ptr, "%s/%s", params->subtree, key);
35 } else {
36 shell_print(params->shell_ptr, "%s", key);
37 }
38
39 return 0;
40 }
41
cmd_list(const struct shell * shell_ptr,size_t argc,char * argv[])42 static int cmd_list(const struct shell *shell_ptr, size_t argc, char *argv[])
43 {
44 int err;
45
46 struct settings_list_callback_params params = {
47 .shell_ptr = shell_ptr,
48 .subtree = (argc == 2 ? argv[1] : NULL)
49 };
50
51 err = settings_load_subtree_direct(params.subtree, settings_list_callback, ¶ms);
52
53 if (err) {
54 shell_error(shell_ptr, "Failed to load settings: %d", err);
55 err = -ENOEXEC;
56 }
57
58 return err;
59 }
60
61 enum settings_value_types {
62 SETTINGS_VALUE_HEX,
63 SETTINGS_VALUE_STRING,
64 };
65
66 struct settings_read_callback_params {
67 const struct shell *shell_ptr;
68 const enum settings_value_types value_type;
69 bool value_found;
70 };
71
settings_read_callback(const char * key,size_t len,settings_read_cb read_cb,void * cb_arg,void * param)72 static int settings_read_callback(const char *key,
73 size_t len,
74 settings_read_cb read_cb,
75 void *cb_arg,
76 void *param)
77 {
78 uint8_t buffer[SETTINGS_MAX_VAL_LEN];
79 ssize_t i;
80 ssize_t num_read_bytes = MIN(len, SETTINGS_MAX_VAL_LEN);
81 struct settings_read_callback_params *params = param;
82
83 /* Process only the exact match and ignore descendants of the searched name */
84 if (settings_name_next(key, NULL) != 0) {
85 return 0;
86 }
87
88 params->value_found = true;
89 num_read_bytes = read_cb(cb_arg, buffer, num_read_bytes);
90
91 if (num_read_bytes < 0) {
92 shell_error(params->shell_ptr, "Failed to read value: %d", (int) num_read_bytes);
93 return 0;
94 }
95
96 if (num_read_bytes == 0) {
97 shell_warn(params->shell_ptr, "Value is empty");
98 return 0;
99 }
100
101 switch (params->value_type) {
102 case SETTINGS_VALUE_HEX:
103 shell_hexdump(params->shell_ptr, buffer, num_read_bytes);
104 break;
105 case SETTINGS_VALUE_STRING:
106 for (i = 0; i < num_read_bytes; i++) {
107 if (!isprint(buffer[i])) {
108 shell_error(params->shell_ptr, "Value is not a string");
109 return 0;
110 }
111 }
112 shell_print(params->shell_ptr, "%.*s", (int)num_read_bytes, buffer);
113 break;
114 }
115
116 if (len > SETTINGS_MAX_VAL_LEN) {
117 shell_print(params->shell_ptr, "(The output has been truncated)");
118 }
119
120 return 0;
121 }
122
settings_parse_type(const char * type,enum settings_value_types * value_type)123 static int settings_parse_type(const char *type, enum settings_value_types *value_type)
124 {
125 if (strcmp(type, "string") == 0) {
126 *value_type = SETTINGS_VALUE_STRING;
127 } else if (strcmp(type, "hex") == 0) {
128 *value_type = SETTINGS_VALUE_HEX;
129 } else {
130 return -EINVAL;
131 }
132
133 return 0;
134 }
135
cmd_read(const struct shell * shell_ptr,size_t argc,char * argv[])136 static int cmd_read(const struct shell *shell_ptr, size_t argc, char *argv[])
137 {
138 int err;
139
140 enum settings_value_types value_type = SETTINGS_VALUE_HEX;
141
142 if (argc > 2) {
143 err = settings_parse_type(argv[1], &value_type);
144 if (err) {
145 shell_error(shell_ptr, "Invalid type: %s", argv[1]);
146 return -EINVAL;
147 }
148 }
149
150 struct settings_read_callback_params params = {
151 .shell_ptr = shell_ptr,
152 .value_type = value_type,
153 .value_found = false
154 };
155
156 err = settings_load_subtree_direct(argv[argc - 1], settings_read_callback, ¶ms);
157
158 if (err) {
159 shell_error(shell_ptr, "Failed to load setting: %d", err);
160 err = -ENOEXEC;
161 } else if (!params.value_found) {
162 shell_error(shell_ptr, "Setting not found");
163 err = -ENOEXEC;
164 }
165
166 return err;
167 }
168
cmd_write(const struct shell * shell_ptr,size_t argc,char * argv[])169 static int cmd_write(const struct shell *shell_ptr, size_t argc, char *argv[])
170 {
171 int err;
172 uint8_t buffer[CONFIG_SHELL_CMD_BUFF_SIZE / 2];
173 const void *value;
174 size_t value_len = 0;
175 enum settings_value_types value_type = SETTINGS_VALUE_HEX;
176
177 if (argc > 3) {
178 err = settings_parse_type(argv[1], &value_type);
179 if (err) {
180 shell_error(shell_ptr, "Invalid type: %s", argv[1]);
181 return -EINVAL;
182 }
183 }
184
185 switch (value_type) {
186 case SETTINGS_VALUE_HEX:
187 value = buffer;
188 value_len = hex2bin(argv[argc - 1], strlen(argv[argc - 1]), buffer, sizeof(buffer));
189 break;
190 case SETTINGS_VALUE_STRING:
191 value = argv[argc - 1];
192 value_len = strlen(argv[argc - 1]);
193 break;
194 }
195
196 if (value_len == 0) {
197 shell_error(shell_ptr, "Failed to parse value");
198 return -EINVAL;
199 }
200
201 err = settings_save_one(argv[argc - 2], value, value_len);
202
203 if (err) {
204 shell_error(shell_ptr, "Failed to write setting: %d", err);
205 err = -ENOEXEC;
206 }
207
208 return err;
209 }
210
cmd_delete(const struct shell * shell_ptr,size_t argc,char * argv[])211 static int cmd_delete(const struct shell *shell_ptr, size_t argc, char *argv[])
212 {
213 int err;
214
215 err = settings_delete(argv[1]);
216
217 if (err) {
218 shell_error(shell_ptr, "Failed to delete setting: %d", err);
219 err = -ENOEXEC;
220 }
221
222 return err;
223 }
224
225 SHELL_STATIC_SUBCMD_SET_CREATE(settings_cmds,
226 SHELL_CMD_ARG(list, NULL,
227 SHELL_HELP("List all settings in a subtree (omit to list all)", "[subtree]"),
228 cmd_list, 1, 1),
229 SHELL_CMD_ARG(read, NULL,
230 SHELL_HELP("Read a specific setting", "[type] <name>\n"
231 "type: string or hex (default: hex)"),
232 cmd_read, 2, 1),
233 SHELL_CMD_ARG(write, NULL,
234 SHELL_HELP("Write to a specific setting",
235 "[type] <name> <value>\n"
236 "type: string or hex (default: hex)"),
237 cmd_write, 3, 1),
238 SHELL_CMD_ARG(delete, NULL, SHELL_HELP("Delete a specific setting", "<name>"), cmd_delete,
239 2, 0),
240 SHELL_SUBCMD_SET_END /* Array terminated. */
241 );
242
243 SHELL_CMD_REGISTER(settings, &settings_cmds, "Settings commands", NULL);
244