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