Coverage Report

Created: 2026-10-17 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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);