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