1 /*
2  * Copyright (c) 2018 Nordic Semiconductor ASA
3  * Copyright (c) 2015 Runtime Inc
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 #include <stdlib.h>
8 #include <string.h>
9 #include <stdio.h>
10 #include <stdbool.h>
11 #include <errno.h>
12 #include <limits.h>
13 #include <zephyr/kernel.h>
14 
15 #include <zephyr/settings/settings.h>
16 #include "settings_priv.h"
17 #include <zephyr/types.h>
18 #include <zephyr/sys/iterable_sections.h>
19 #include <zephyr/logging/log.h>
20 LOG_MODULE_REGISTER(settings, CONFIG_SETTINGS_LOG_LEVEL);
21 
22 #if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS)
23 sys_slist_t settings_handlers;
24 #endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */
25 
26 #ifdef CONFIG_MULTITHREADING
27 static K_MUTEX_DEFINE(settings_lock);
28 #endif
29 
30 void settings_store_init(void);
31 
settings_init(void)32 void settings_init(void)
33 {
34 #if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS)
35 	sys_slist_init(&settings_handlers);
36 #endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */
37 	settings_store_init();
38 }
39 
40 #if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS)
settings_register_with_cprio(struct settings_handler * handler,int cprio)41 int settings_register_with_cprio(struct settings_handler *handler, int cprio)
42 {
43 	int rc = 0;
44 
45 	STRUCT_SECTION_FOREACH(settings_handler_static, ch) {
46 		if (strcmp(handler->name, ch->name) == 0) {
47 			return -EEXIST;
48 		}
49 	}
50 
51 	settings_lock_take();
52 
53 	struct settings_handler *ch;
54 	SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) {
55 		if (strcmp(handler->name, ch->name) == 0) {
56 			rc = -EEXIST;
57 			goto end;
58 		}
59 	}
60 
61 	handler->cprio = cprio;
62 	sys_slist_append(&settings_handlers, &handler->node);
63 
64 end:
65 	settings_lock_release();
66 	return rc;
67 }
68 
settings_register(struct settings_handler * handler)69 int settings_register(struct settings_handler *handler)
70 {
71 	return settings_register_with_cprio(handler, 0);
72 }
73 #endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */
74 
settings_name_steq(const char * name,const char * key,const char ** next)75 int settings_name_steq(const char *name, const char *key, const char **next)
76 {
77 	if (next) {
78 		*next = NULL;
79 	}
80 
81 	if ((!name) || (!key)) {
82 		return 0;
83 	}
84 
85 	/* name might come from flash directly, in flash the name would end
86 	 * with '=' or '\0' depending how storage is done. Flash reading is
87 	 * limited to what can be read
88 	 */
89 
90 	while ((*key != '\0') && (*key == *name) &&
91 	       (*name != '\0') && (*name != SETTINGS_NAME_END)) {
92 		key++;
93 		name++;
94 	}
95 
96 	if (*key != '\0') {
97 		return 0;
98 	}
99 
100 	if (*name == SETTINGS_NAME_SEPARATOR) {
101 		if (next) {
102 			*next = name + 1;
103 		}
104 		return 1;
105 	}
106 
107 	if ((*name == SETTINGS_NAME_END) || (*name == '\0')) {
108 		return 1;
109 	}
110 
111 	return 0;
112 }
113 
settings_name_next(const char * name,const char ** next)114 int settings_name_next(const char *name, const char **next)
115 {
116 	int rc = 0;
117 
118 	if (next) {
119 		*next = NULL;
120 	}
121 
122 	if (!name) {
123 		return 0;
124 	}
125 
126 	/* name might come from flash directly, in flash the name would end
127 	 * with '=' or '\0' depending how storage is done. Flash reading is
128 	 * limited to what can be read
129 	 */
130 	while ((*name != '\0') && (*name != SETTINGS_NAME_END) &&
131 	       (*name != SETTINGS_NAME_SEPARATOR)) {
132 		rc++;
133 		name++;
134 	}
135 
136 	if (*name == SETTINGS_NAME_SEPARATOR) {
137 		if (next) {
138 			*next = name + 1;
139 		}
140 		return rc;
141 	}
142 
143 	return rc;
144 }
145 
settings_parse_and_lookup(const char * name,const char ** next)146 struct settings_handler_static *settings_parse_and_lookup(const char *name,
147 							const char **next)
148 {
149 	struct settings_handler_static *bestmatch;
150 	const char *tmpnext;
151 
152 	bestmatch = NULL;
153 	if (next) {
154 		*next = NULL;
155 	}
156 
157 	STRUCT_SECTION_FOREACH(settings_handler_static, ch) {
158 		if (!settings_name_steq(name, ch->name, &tmpnext)) {
159 			continue;
160 		}
161 		if (!bestmatch) {
162 			bestmatch = ch;
163 			if (next) {
164 				*next = tmpnext;
165 			}
166 			continue;
167 		}
168 		if (settings_name_steq(ch->name, bestmatch->name, NULL)) {
169 			bestmatch = ch;
170 			if (next) {
171 				*next = tmpnext;
172 			}
173 		}
174 	}
175 
176 #if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS)
177 	struct settings_handler *ch;
178 
179 	SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) {
180 		if (!settings_name_steq(name, ch->name, &tmpnext)) {
181 			continue;
182 		}
183 		if (!bestmatch) {
184 			bestmatch = (struct settings_handler_static *)ch;
185 			if (next) {
186 				*next = tmpnext;
187 			}
188 			continue;
189 		}
190 		if (settings_name_steq(ch->name, bestmatch->name, NULL)) {
191 			bestmatch = (struct settings_handler_static *)ch;
192 			if (next) {
193 				*next = tmpnext;
194 			}
195 		}
196 	}
197 #endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */
198 	return bestmatch;
199 }
200 
settings_call_set_handler(const char * name,size_t len,settings_read_cb read_cb,void * read_cb_arg,const struct settings_load_arg * load_arg)201 int settings_call_set_handler(const char *name,
202 			      size_t len,
203 			      settings_read_cb read_cb,
204 			      void *read_cb_arg,
205 			      const struct settings_load_arg *load_arg)
206 {
207 	int rc;
208 	const char *name_key = name;
209 
210 	if (load_arg && load_arg->subtree &&
211 	    !settings_name_steq(name, load_arg->subtree, &name_key)) {
212 		return 0;
213 	}
214 
215 	if (load_arg && load_arg->cb) {
216 		rc = load_arg->cb(name_key, len, read_cb, read_cb_arg,
217 				  load_arg->param);
218 	} else {
219 		struct settings_handler_static *ch;
220 
221 		ch = settings_parse_and_lookup(name, &name_key);
222 		if (!ch) {
223 			return 0;
224 		}
225 
226 		rc = ch->h_set(name_key, len, read_cb, read_cb_arg);
227 
228 		if (rc != 0) {
229 			LOG_ERR("set-value failure. key: %s error(%d)",
230 				name, rc);
231 			/* Ignoring the error */
232 			rc = 0;
233 		} else {
234 			LOG_DBG("set-value OK. key: %s",
235 				name);
236 		}
237 	}
238 	return rc;
239 }
240 
settings_commit(void)241 int settings_commit(void)
242 {
243 	return settings_commit_subtree(NULL);
244 }
245 
set_next_cprio(int handler_cprio,int cprio,int next_cprio)246 static int set_next_cprio(int handler_cprio, int cprio, int next_cprio)
247 {
248 	if (handler_cprio <= cprio) {
249 		return next_cprio;
250 	}
251 
252 	/* If cprio and next_cprio are identical then next_cprio has not
253 	 * yet been set to any value and its initialized to the first
254 	 * handler_cprio above cprio.
255 	 */
256 	if (cprio == next_cprio) {
257 		return handler_cprio;
258 	}
259 
260 	return MIN(handler_cprio, next_cprio);
261 }
262 
settings_commit_subtree(const char * subtree)263 int settings_commit_subtree(const char *subtree)
264 {
265 	int rc;
266 	int rc2;
267 	int cprio = INT_MIN;
268 
269 	rc = 0;
270 
271 	while (true) {
272 		int next_cprio = cprio;
273 
274 		STRUCT_SECTION_FOREACH(settings_handler_static, ch) {
275 			if (subtree && !settings_name_steq(ch->name, subtree, NULL)) {
276 				continue;
277 			}
278 
279 			if (ch->h_commit) {
280 				next_cprio = set_next_cprio(ch->cprio, cprio, next_cprio);
281 				if (ch->cprio != cprio) {
282 					continue;
283 				}
284 
285 				rc2 = ch->h_commit();
286 				if (!rc) {
287 					rc = rc2;
288 				}
289 			}
290 		}
291 
292 		if (IS_ENABLED(CONFIG_SETTINGS_DYNAMIC_HANDLERS)) {
293 			struct settings_handler *ch;
294 
295 			SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) {
296 				if (subtree && !settings_name_steq(ch->name, subtree, NULL)) {
297 					continue;
298 				}
299 
300 				if (ch->h_commit) {
301 					next_cprio = set_next_cprio(ch->cprio, cprio, next_cprio);
302 					if (ch->cprio != cprio) {
303 						continue;
304 					}
305 
306 					rc2 = ch->h_commit();
307 					if (!rc) {
308 						rc = rc2;
309 					}
310 				}
311 			}
312 		}
313 
314 		if (cprio == next_cprio) {
315 			break;
316 		}
317 
318 		cprio = next_cprio;
319 	}
320 
321 	return rc;
322 }
323 
settings_lock_take(void)324 void settings_lock_take(void)
325 {
326 #ifdef CONFIG_MULTITHREADING
327 	k_mutex_lock(&settings_lock, K_FOREVER);
328 #endif
329 }
330 
settings_lock_release(void)331 void settings_lock_release(void)
332 {
333 #ifdef CONFIG_MULTITHREADING
334 	k_mutex_unlock(&settings_lock);
335 #endif
336 }
337