/src/barebox/common/env.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-only |
2 | | /* |
3 | | * env.c - environment variables storage |
4 | | * |
5 | | * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix |
6 | | */ |
7 | | |
8 | | /** |
9 | | * @file |
10 | | * @brief Environment support |
11 | | */ |
12 | | |
13 | | #include <common.h> |
14 | | #include <driver.h> |
15 | | #include <malloc.h> |
16 | | #include <xfuncs.h> |
17 | | #include <errno.h> |
18 | | #include <init.h> |
19 | | #include <string.h> |
20 | | #include <environment.h> |
21 | | |
22 | | static struct env_context root = { |
23 | | .local = LIST_HEAD_INIT(root.local), |
24 | | .global = LIST_HEAD_INIT(root.global), |
25 | | }; |
26 | | |
27 | | static struct env_context *context = &root; |
28 | | |
29 | | /** |
30 | | * Remove a list of environment variables |
31 | | * @param[in] v Variable anchor to remove |
32 | | */ |
33 | | static void free_context(struct env_context *c) |
34 | 0 | { |
35 | 0 | struct variable_d *v, *tmp; |
36 | |
|
37 | 0 | list_for_each_entry_safe(v, tmp, &c->local, list) { |
38 | 0 | free_const(v->name); |
39 | 0 | free_const(v->data); |
40 | 0 | list_del(&v->list); |
41 | 0 | free(v); |
42 | 0 | } |
43 | |
|
44 | 0 | list_for_each_entry_safe(v, tmp, &c->global, list) { |
45 | 0 | free_const(v->name); |
46 | 0 | free_const(v->data); |
47 | 0 | list_del(&v->list); |
48 | 0 | free(v); |
49 | 0 | } |
50 | |
|
51 | 0 | free(c); |
52 | 0 | } |
53 | | |
54 | | /** Read back current context */ |
55 | | struct env_context *get_current_context(void) |
56 | 0 | { |
57 | 0 | return context; |
58 | 0 | } |
59 | | EXPORT_SYMBOL(get_current_context); |
60 | | |
61 | | |
62 | | /** |
63 | | * Create a new variable context and put it on the stack |
64 | | */ |
65 | | int env_push_context(void) |
66 | 0 | { |
67 | 0 | struct env_context *c = xzalloc(sizeof(struct env_context)); |
68 | |
|
69 | 0 | INIT_LIST_HEAD(&c->local); |
70 | 0 | INIT_LIST_HEAD(&c->global); |
71 | |
|
72 | 0 | c->parent = context; |
73 | 0 | context = c; |
74 | |
|
75 | 0 | return 0; |
76 | 0 | } |
77 | | |
78 | | /** |
79 | | * free current variable context and restore the previous one |
80 | | */ |
81 | | int env_pop_context(void) |
82 | 0 | { |
83 | 0 | struct env_context *c; |
84 | |
|
85 | 0 | if (context->parent) { |
86 | 0 | c = context->parent; |
87 | 0 | free_context(context); |
88 | 0 | context = c; |
89 | 0 | return 0; |
90 | 0 | } |
91 | 0 | return -EINVAL; |
92 | 0 | } |
93 | | |
94 | | /** |
95 | | * Return variable's value |
96 | | * @param[in] var Variable of interest |
97 | | * @return Value as text |
98 | | */ |
99 | | const char *var_val(struct variable_d *var) |
100 | 52 | { |
101 | 52 | return var->data; |
102 | 52 | } |
103 | | |
104 | | /** |
105 | | * Return variable's name |
106 | | * @param[in] var Variable of interest |
107 | | * @return Name as text |
108 | | */ |
109 | | const char *var_name(struct variable_d *var) |
110 | 52 | { |
111 | 52 | return var->name; |
112 | 52 | } |
113 | | |
114 | | static const char *getenv_raw(struct list_head *l, const char *name) |
115 | 104 | { |
116 | 104 | struct variable_d *v; |
117 | | |
118 | 104 | list_for_each_entry(v, l, list) { |
119 | 52 | if (!strcmp(var_name(v), name)) |
120 | 52 | return var_val(v); |
121 | 52 | } |
122 | | |
123 | 52 | return NULL; |
124 | 104 | } |
125 | | |
126 | | static int getenv_raw_call(int (*fn)(struct variable_d *v, void *data), |
127 | | struct list_head *l, void *data) |
128 | 0 | { |
129 | 0 | struct variable_d *v; |
130 | 0 | int ret; |
131 | |
|
132 | 0 | list_for_each_entry(v, l, list) { |
133 | 0 | ret = fn(v, data); |
134 | 0 | if (ret) |
135 | 0 | return ret; |
136 | 0 | } |
137 | | |
138 | 0 | return 0; |
139 | 0 | } |
140 | | |
141 | | static const char *dev_getenv(const char *name) |
142 | 0 | { |
143 | 0 | const char *pos, *val, *dot, *varname; |
144 | 0 | char *devname; |
145 | 0 | struct device *dev; |
146 | |
|
147 | 0 | pos = name; |
148 | |
|
149 | 0 | while (1) { |
150 | 0 | dot = strchr(pos, '.'); |
151 | 0 | if (!dot) |
152 | 0 | break; |
153 | | |
154 | 0 | devname = xstrndup(name, dot - name); |
155 | 0 | varname = dot + 1; |
156 | |
|
157 | 0 | dev = get_device_by_name(devname); |
158 | |
|
159 | 0 | free(devname); |
160 | |
|
161 | 0 | if (dev) { |
162 | 0 | val = dev_get_param(dev, varname); |
163 | 0 | if (val) |
164 | 0 | return val; |
165 | 0 | } |
166 | | |
167 | 0 | pos = dot + 1; |
168 | 0 | } |
169 | | |
170 | 0 | return NULL; |
171 | 0 | } |
172 | | |
173 | | const char *getenv(const char *name) |
174 | 0 | { |
175 | 0 | struct env_context *c; |
176 | 0 | const char *val; |
177 | |
|
178 | 0 | if (strchr(name, '.')) |
179 | 0 | return dev_getenv(name); |
180 | | |
181 | 0 | c = context; |
182 | |
|
183 | 0 | val = getenv_raw(&c->local, name); |
184 | 0 | if (val) |
185 | 0 | return val; |
186 | | |
187 | 0 | while (c) { |
188 | 0 | val = getenv_raw(&c->global, name); |
189 | 0 | if (val) |
190 | 0 | return val; |
191 | 0 | c = c->parent; |
192 | 0 | } |
193 | 0 | return NULL; |
194 | 0 | } |
195 | | EXPORT_SYMBOL(getenv); |
196 | | |
197 | | int envvar_for_each(int (*fn)(struct variable_d *v, void *data), void *data) |
198 | 0 | { |
199 | 0 | struct env_context *c; |
200 | 0 | int ret; |
201 | |
|
202 | 0 | c = context; |
203 | |
|
204 | 0 | ret = getenv_raw_call(fn, &c->local, data); |
205 | 0 | if (ret) |
206 | 0 | return ret; |
207 | | |
208 | 0 | while (c) { |
209 | 0 | ret = getenv_raw_call(fn, &c->global, data); |
210 | 0 | if (ret) |
211 | 0 | return ret; |
212 | 0 | c = c->parent; |
213 | 0 | } |
214 | | |
215 | 0 | return 0; |
216 | 0 | } |
217 | | EXPORT_SYMBOL(envvar_for_each); |
218 | | |
219 | | static int setenv_raw(struct list_head *l, const char *name, const char *value) |
220 | 156 | { |
221 | 156 | struct variable_d *v; |
222 | | |
223 | 156 | list_for_each_entry(v, l, list) { |
224 | 104 | if (!strcmp(v->name, name)) { |
225 | 52 | if (value) { |
226 | 0 | free_const(v->data); |
227 | 0 | v->data = xstrdup_const(value); |
228 | |
|
229 | 0 | return 0; |
230 | 52 | } else { |
231 | 52 | list_del(&v->list); |
232 | 52 | free_const(v->name); |
233 | 52 | free_const(v->data); |
234 | 52 | free(v); |
235 | | |
236 | 52 | return 0; |
237 | 52 | } |
238 | 52 | } |
239 | 104 | } |
240 | | |
241 | 104 | if (value) { |
242 | 104 | v = xzalloc(sizeof(*v)); |
243 | 104 | v->name = xstrdup_const(name); |
244 | 104 | v->data = xstrdup_const(value); |
245 | 104 | list_add_tail(&v->list, l); |
246 | 104 | } |
247 | | |
248 | 104 | return 0; |
249 | 156 | } |
250 | | |
251 | | static int dev_setenv(const char *name, const char *val) |
252 | 0 | { |
253 | 0 | const char *pos, *dot, *varname; |
254 | 0 | char *devname; |
255 | 0 | struct device *dev; |
256 | |
|
257 | 0 | pos = name; |
258 | |
|
259 | 0 | while (1) { |
260 | 0 | dot = strchr(pos, '.'); |
261 | 0 | if (!dot) |
262 | 0 | break; |
263 | | |
264 | 0 | devname = xstrndup(name, dot - name); |
265 | 0 | varname = dot + 1; |
266 | |
|
267 | 0 | dev = get_device_by_name(devname); |
268 | |
|
269 | 0 | free(devname); |
270 | |
|
271 | 0 | if (dev) { |
272 | 0 | if (get_param_by_name(dev, varname)) |
273 | 0 | return dev_set_param(dev, varname, val); |
274 | 0 | } |
275 | | |
276 | 0 | pos = dot + 1; |
277 | 0 | } |
278 | | |
279 | 0 | return -ENODEV; |
280 | 0 | } |
281 | | |
282 | | /** |
283 | | * setenv - set environment variables |
284 | | * @name - Variable name |
285 | | * @value - the value to set, empty string not handled specially |
286 | | * |
287 | | * Returns 0 for success and a negative error code otherwise |
288 | | * Use unsetenv() to unset. |
289 | | */ |
290 | | int setenv(const char *_name, const char *value) |
291 | 52 | { |
292 | 52 | const char *name = strdup_const(_name); |
293 | 52 | int ret = 0; |
294 | 52 | struct list_head *list; |
295 | 52 | const char *dot; |
296 | | |
297 | 52 | dot = strchr(name, '.'); |
298 | 52 | if (dot) { |
299 | 0 | ret = dev_setenv(name, value); |
300 | 0 | if (IS_ENABLED(CONFIG_LONGHELP) && ret == -ENODEV && |
301 | 0 | (strstarts(name, "nv.") || strstarts(name, "global."))) { |
302 | 0 | eprintf("Parameter not found. Did you mean to create it with: %.*s %s='%s' ?\n", |
303 | 0 | (int)(dot - name), name, dot + 1, value); |
304 | 0 | } else if (ret) { |
305 | 0 | eprintf("Cannot set parameter %s: %pe\n", name, ERR_PTR(ret)); |
306 | 0 | } |
307 | |
|
308 | 0 | goto out; |
309 | 0 | } |
310 | | |
311 | 52 | if (getenv_raw(&context->global, name)) |
312 | 0 | list = &context->global; |
313 | 52 | else |
314 | 52 | list = &context->local; |
315 | | |
316 | 52 | ret = setenv_raw(list, name, value); |
317 | 52 | out: |
318 | 52 | free_const(name); |
319 | | |
320 | 52 | return ret; |
321 | 52 | } |
322 | | EXPORT_SYMBOL(setenv); |
323 | | |
324 | | /** |
325 | | * pr_setenv - set environment variables |
326 | | * @name - Variable name |
327 | | * @fmt - the format string to use |
328 | | * |
329 | | * Returns 0 for success and a negative error code otherwise |
330 | | * Use unsetenv() to unset. |
331 | | */ |
332 | | int pr_setenv(const char *name, const char *fmt, ...) |
333 | 0 | { |
334 | 0 | va_list ap; |
335 | 0 | int ret = 0; |
336 | 0 | char *value; |
337 | 0 | int len; |
338 | |
|
339 | 0 | va_start(ap, fmt); |
340 | 0 | len = vasprintf(&value, fmt, ap); |
341 | 0 | va_end(ap); |
342 | |
|
343 | 0 | if (len < 0) |
344 | 0 | return -ENOMEM; |
345 | | |
346 | 0 | ret = setenv(name, value); |
347 | 0 | free(value); |
348 | |
|
349 | 0 | return ret; |
350 | 0 | } |
351 | | EXPORT_SYMBOL(pr_setenv); |
352 | | |
353 | | int export(const char *varname) |
354 | 52 | { |
355 | 52 | const char *val = getenv_raw(&context->local, varname); |
356 | | |
357 | 52 | if (val) { |
358 | 52 | setenv_raw(&context->global, varname, val); |
359 | 52 | setenv_raw(&context->local, varname, NULL); |
360 | 52 | } |
361 | 52 | return 0; |
362 | 52 | } |
363 | | EXPORT_SYMBOL(export); |
364 | | |
365 | | void export_env_ull(const char *name, unsigned long long val) |
366 | 0 | { |
367 | 0 | char *valstr = basprintf("%llu", val); |
368 | |
|
369 | 0 | setenv(name, valstr); |
370 | 0 | export(name); |
371 | |
|
372 | 0 | kfree(valstr); |
373 | 0 | } |
374 | | EXPORT_SYMBOL(export_env_ull); |
375 | | |
376 | | /* |
377 | | * Like regular getenv, but never returns an empty string. |
378 | | * If the string is empty, NULL is returned instead |
379 | | */ |
380 | | const char *getenv_nonempty(const char *var) |
381 | 0 | { |
382 | 0 | const char *val = getenv(var); |
383 | |
|
384 | 0 | if (isempty(val)) |
385 | 0 | return NULL; |
386 | | |
387 | 0 | return val; |
388 | 0 | } |
389 | | EXPORT_SYMBOL(getenv_nonempty); |
390 | | |
391 | | static int getenv_ull_base(const char *var, int base, unsigned long long *val) |
392 | 0 | { |
393 | 0 | const char *valstr = getenv(var); |
394 | |
|
395 | 0 | if (isempty(valstr)) |
396 | 0 | return -EINVAL; |
397 | | |
398 | 0 | *val = simple_strtoull(valstr, NULL, base); |
399 | |
|
400 | 0 | return 0; |
401 | 0 | } |
402 | | |
403 | | int getenv_ull(const char *var , unsigned long long *val) |
404 | 0 | { |
405 | 0 | return getenv_ull_base(var, 0, val); |
406 | 0 | } |
407 | | EXPORT_SYMBOL(getenv_ull); |
408 | | |
409 | | int getenv_ullx(const char *var , unsigned long long *val) |
410 | 0 | { |
411 | 0 | return getenv_ull_base(var, 16, val); |
412 | 0 | } |
413 | | EXPORT_SYMBOL(getenv_ullx); |
414 | | |
415 | | int getenv_ul(const char *var , unsigned long *val) |
416 | 0 | { |
417 | 0 | const char *valstr = getenv(var); |
418 | |
|
419 | 0 | if (isempty(valstr)) |
420 | 0 | return -EINVAL; |
421 | | |
422 | 0 | *val = simple_strtoul(valstr, NULL, 0); |
423 | |
|
424 | 0 | return 0; |
425 | 0 | } |
426 | | EXPORT_SYMBOL(getenv_ul); |
427 | | |
428 | | int getenv_uint(const char *var , unsigned int *val) |
429 | 0 | { |
430 | 0 | const char *valstr = getenv(var); |
431 | |
|
432 | 0 | if (isempty(valstr)) |
433 | 0 | return -EINVAL; |
434 | | |
435 | 0 | *val = simple_strtoul(valstr, NULL, 0); |
436 | |
|
437 | 0 | return 0; |
438 | 0 | } |
439 | | EXPORT_SYMBOL(getenv_uint); |
440 | | |
441 | | /** |
442 | | * getenv_bool - get a boolean value from an environment variable |
443 | | * @var - Variable name |
444 | | * @val - The boolean value returned. |
445 | | * |
446 | | * This function treats |
447 | | * - any positive (nonzero) number as true |
448 | | * - "0" as false |
449 | | * - "true" (case insensitive) as true |
450 | | * - "false" (case insensitive) as false |
451 | | * |
452 | | * Returns 0 for success or negative error code if the variable does |
453 | | * not exist or contains something this function does not recognize |
454 | | * as true or false. |
455 | | */ |
456 | | int getenv_bool(const char *var, int *val) |
457 | 0 | { |
458 | 0 | const char *valstr = getenv(var); |
459 | |
|
460 | 0 | return strtobool(valstr, val); |
461 | 0 | } |
462 | | EXPORT_SYMBOL(getenv_bool); |