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, &params);
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, &params);
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