]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/env-util.c
util-lib: split out allocation calls into alloc-util.[ch]
[thirdparty/systemd.git] / src / basic / env-util.c
CommitLineData
4d1a6904
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <limits.h>
4d1a6904
LP
23#include <unistd.h>
24
b5efdb8a 25#include "alloc-util.h"
07630cea 26#include "def.h"
b5efdb8a 27#include "env-util.h"
07630cea 28#include "string-util.h"
4d1a6904
LP
29#include "strv.h"
30#include "utf8.h"
31#include "util.h"
4d1a6904
LP
32
33#define VALID_CHARS_ENV_NAME \
4b549144 34 DIGITS LETTERS \
4d1a6904
LP
35 "_"
36
37#ifndef ARG_MAX
38#define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX))
39#endif
40
41static bool env_name_is_valid_n(const char *e, size_t n) {
42 const char *p;
43
44 if (!e)
45 return false;
46
47 if (n <= 0)
48 return false;
49
50 if (e[0] >= '0' && e[0] <= '9')
51 return false;
52
53 /* POSIX says the overall size of the environment block cannot
54 * be > ARG_MAX, an individual assignment hence cannot be
55 * either. Discounting the equal sign and trailing NUL this
56 * hence leaves ARG_MAX-2 as longest possible variable
57 * name. */
58 if (n > ARG_MAX - 2)
59 return false;
60
61 for (p = e; p < e + n; p++)
62 if (!strchr(VALID_CHARS_ENV_NAME, *p))
63 return false;
64
65 return true;
66}
67
68bool env_name_is_valid(const char *e) {
69 if (!e)
70 return false;
71
72 return env_name_is_valid_n(e, strlen(e));
73}
74
75bool env_value_is_valid(const char *e) {
76 if (!e)
77 return false;
78
79 if (!utf8_is_valid(e))
80 return false;
81
6294aa76
LP
82 /* bash allows tabs in environment variables, and so should
83 * we */
84 if (string_has_cc(e, "\t"))
4d1a6904
LP
85 return false;
86
87 /* POSIX says the overall size of the environment block cannot
88 * be > ARG_MAX, an individual assignment hence cannot be
89 * either. Discounting the shortest possible variable name of
90 * length 1, the equal sign and trailing NUL this hence leaves
91 * ARG_MAX-3 as longest possible variable value. */
92 if (strlen(e) > ARG_MAX - 3)
93 return false;
94
95 return true;
96}
97
98bool env_assignment_is_valid(const char *e) {
99 const char *eq;
100
101 eq = strchr(e, '=');
102 if (!eq)
103 return false;
104
105 if (!env_name_is_valid_n(e, eq - e))
106 return false;
107
108 if (!env_value_is_valid(eq + 1))
109 return false;
110
111 /* POSIX says the overall size of the environment block cannot
112 * be > ARG_MAX, hence the individual variable assignments
5f9cfd4c 113 * cannot be either, but let's leave room for one trailing NUL
4d1a6904
LP
114 * byte. */
115 if (strlen(e) > ARG_MAX - 1)
116 return false;
117
118 return true;
119}
120
121bool strv_env_is_valid(char **e) {
122 char **p, **q;
123
124 STRV_FOREACH(p, e) {
125 size_t k;
126
127 if (!env_assignment_is_valid(*p))
128 return false;
129
130 /* Check if there are duplicate assginments */
131 k = strcspn(*p, "=");
132 STRV_FOREACH(q, p + 1)
641906e9 133 if (strneq(*p, *q, k) && (*q)[k] == '=')
4d1a6904
LP
134 return false;
135 }
136
137 return true;
138}
139
123b964a
LP
140bool strv_env_name_or_assignment_is_valid(char **l) {
141 char **p, **q;
142
143 STRV_FOREACH(p, l) {
144 if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
145 return false;
146
147 STRV_FOREACH(q, p + 1)
148 if (streq(*p, *q))
149 return false;
150 }
151
152 return true;
153}
154
4d1a6904
LP
155static int env_append(char **r, char ***k, char **a) {
156 assert(r);
157 assert(k);
158
159 if (!a)
160 return 0;
161
162 /* Add the entries of a to *k unless they already exist in *r
163 * in which case they are overridden instead. This assumes
164 * there is enough space in the r array. */
165
166 for (; *a; a++) {
167 char **j;
168 size_t n;
169
170 n = strcspn(*a, "=");
171
172 if ((*a)[n] == '=')
173 n++;
174
175 for (j = r; j < *k; j++)
641906e9 176 if (strneq(*j, *a, n))
4d1a6904
LP
177 break;
178
179 if (j >= *k)
180 (*k)++;
181 else
182 free(*j);
183
184 *j = strdup(*a);
185 if (!*j)
186 return -ENOMEM;
187 }
188
189 return 0;
190}
191
192char **strv_env_merge(unsigned n_lists, ...) {
193 size_t n = 0;
194 char **l, **k, **r;
195 va_list ap;
196 unsigned i;
197
198 /* Merges an arbitrary number of environment sets */
199
200 va_start(ap, n_lists);
201 for (i = 0; i < n_lists; i++) {
202 l = va_arg(ap, char**);
203 n += strv_length(l);
204 }
205 va_end(ap);
206
207 r = new(char*, n+1);
208 if (!r)
209 return NULL;
210
211 k = r;
212
213 va_start(ap, n_lists);
214 for (i = 0; i < n_lists; i++) {
215 l = va_arg(ap, char**);
216 if (env_append(r, &k, l) < 0)
217 goto fail;
218 }
219 va_end(ap);
220
221 *k = NULL;
222
223 return r;
224
225fail:
226 va_end(ap);
227 strv_free(r);
228
229 return NULL;
230}
231
44a6b1b6 232_pure_ static bool env_match(const char *t, const char *pattern) {
4d1a6904
LP
233 assert(t);
234 assert(pattern);
235
236 /* pattern a matches string a
237 * a matches a=
238 * a matches a=b
239 * a= matches a=
240 * a=b matches a=b
241 * a= does not match a
242 * a=b does not match a=
243 * a=b does not match a
244 * a=b does not match a=c */
245
246 if (streq(t, pattern))
247 return true;
248
249 if (!strchr(pattern, '=')) {
250 size_t l = strlen(pattern);
251
641906e9 252 return strneq(t, pattern, l) && t[l] == '=';
4d1a6904
LP
253 }
254
255 return false;
256}
257
258char **strv_env_delete(char **x, unsigned n_lists, ...) {
259 size_t n, i = 0;
260 char **k, **r;
261 va_list ap;
262
263 /* Deletes every entry from x that is mentioned in the other
264 * string lists */
265
266 n = strv_length(x);
267
268 r = new(char*, n+1);
269 if (!r)
270 return NULL;
271
272 STRV_FOREACH(k, x) {
273 unsigned v;
274
275 va_start(ap, n_lists);
276 for (v = 0; v < n_lists; v++) {
277 char **l, **j;
278
279 l = va_arg(ap, char**);
280 STRV_FOREACH(j, l)
281 if (env_match(*k, *j))
282 goto skip;
283 }
284 va_end(ap);
285
286 r[i] = strdup(*k);
287 if (!r[i]) {
288 strv_free(r);
289 return NULL;
290 }
291
292 i++;
293 continue;
294
295 skip:
296 va_end(ap);
297 }
298
299 r[i] = NULL;
300
301 assert(i <= n);
302
303 return r;
304}
305
306char **strv_env_unset(char **l, const char *p) {
307
308 char **f, **t;
309
310 if (!l)
311 return NULL;
312
313 assert(p);
314
315 /* Drops every occurrence of the env var setting p in the
43d03a83 316 * string list. Edits in-place. */
4d1a6904
LP
317
318 for (f = t = l; *f; f++) {
319
320 if (env_match(*f, p)) {
321 free(*f);
322 continue;
323 }
324
325 *(t++) = *f;
326 }
43d03a83
LP
327
328 *t = NULL;
329 return l;
330}
331
332char **strv_env_unset_many(char **l, ...) {
333
334 char **f, **t;
335
336 if (!l)
337 return NULL;
338
339 /* Like strv_env_unset() but applies many at once. Edits in-place. */
340
341 for (f = t = l; *f; f++) {
342 bool found = false;
343 const char *p;
344 va_list ap;
345
346 va_start(ap, l);
347
348 while ((p = va_arg(ap, const char*))) {
349 if (env_match(*f, p)) {
350 found = true;
351 break;
352 }
353 }
354
355 va_end(ap);
356
357 if (found) {
358 free(*f);
359 continue;
360 }
361
362 *(t++) = *f;
363 }
4d1a6904
LP
364
365 *t = NULL;
366 return l;
367}
368
369char **strv_env_set(char **x, const char *p) {
370
371 char **k, **r;
372 char* m[2] = { (char*) p, NULL };
373
374 /* Overrides the env var setting of p, returns a new copy */
375
376 r = new(char*, strv_length(x)+2);
377 if (!r)
378 return NULL;
379
380 k = r;
381 if (env_append(r, &k, x) < 0)
382 goto fail;
383
384 if (env_append(r, &k, m) < 0)
385 goto fail;
386
387 *k = NULL;
388
389 return r;
390
391fail:
392 strv_free(r);
393 return NULL;
394}
395
396char *strv_env_get_n(char **l, const char *name, size_t k) {
397 char **i;
398
399 assert(name);
400
401 if (k <= 0)
402 return NULL;
403
404 STRV_FOREACH(i, l)
641906e9 405 if (strneq(*i, name, k) &&
4d1a6904
LP
406 (*i)[k] == '=')
407 return *i + k + 1;
408
409 return NULL;
410}
411
412char *strv_env_get(char **l, const char *name) {
413 assert(name);
414
415 return strv_env_get_n(l, name, strlen(name));
416}
417
039f0e70 418char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
4d1a6904
LP
419 char **p, **q;
420 int k = 0;
421
422 STRV_FOREACH(p, e) {
423 size_t n;
424 bool duplicate = false;
425
426 if (!env_assignment_is_valid(*p)) {
039f0e70
LP
427 if (invalid_callback)
428 invalid_callback(*p, userdata);
4d1a6904
LP
429 free(*p);
430 continue;
431 }
432
433 n = strcspn(*p, "=");
434 STRV_FOREACH(q, p + 1)
641906e9 435 if (strneq(*p, *q, n) && (*q)[n] == '=') {
4d1a6904
LP
436 duplicate = true;
437 break;
438 }
439
440 if (duplicate) {
441 free(*p);
442 continue;
443 }
444
445 e[k++] = *p;
446 }
447
5b4fb02d
LP
448 if (e)
449 e[k] = NULL;
450
4d1a6904
LP
451 return e;
452}
3c800095
RC
453
454char *replace_env(const char *format, char **env) {
455 enum {
456 WORD,
457 CURLY,
458 VARIABLE
459 } state = WORD;
460
461 const char *e, *word = format;
462 char *r = NULL, *k;
463
464 assert(format);
465
466 for (e = format; *e; e ++) {
467
468 switch (state) {
469
470 case WORD:
471 if (*e == '$')
472 state = CURLY;
473 break;
474
475 case CURLY:
476 if (*e == '{') {
477 k = strnappend(r, word, e-word-1);
478 if (!k)
479 goto fail;
480
481 free(r);
482 r = k;
483
484 word = e-1;
485 state = VARIABLE;
486
487 } else if (*e == '$') {
488 k = strnappend(r, word, e-word);
489 if (!k)
490 goto fail;
491
492 free(r);
493 r = k;
494
495 word = e+1;
496 state = WORD;
497 } else
498 state = WORD;
499 break;
500
501 case VARIABLE:
502 if (*e == '}') {
503 const char *t;
504
505 t = strempty(strv_env_get_n(env, word+2, e-word-2));
506
507 k = strappend(r, t);
508 if (!k)
509 goto fail;
510
511 free(r);
512 r = k;
513
514 word = e+1;
515 state = WORD;
516 }
517 break;
518 }
519 }
520
521 k = strnappend(r, word, e-word);
522 if (!k)
523 goto fail;
524
525 free(r);
526 return k;
527
528fail:
529 free(r);
530 return NULL;
531}
532
533char **replace_env_argv(char **argv, char **env) {
534 char **ret, **i;
535 unsigned k = 0, l = 0;
536
537 l = strv_length(argv);
538
539 ret = new(char*, l+1);
540 if (!ret)
541 return NULL;
542
543 STRV_FOREACH(i, argv) {
544
545 /* If $FOO appears as single word, replace it by the split up variable */
df553b58 546 if ((*i)[0] == '$' && (*i)[1] != '{' && (*i)[1] != '$') {
3c800095
RC
547 char *e;
548 char **w, **m = NULL;
549 unsigned q;
550
551 e = strv_env_get(env, *i+1);
552 if (e) {
553 int r;
554
8adaf7bd 555 r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
3c800095
RC
556 if (r < 0) {
557 ret[k] = NULL;
558 strv_free(ret);
559 return NULL;
560 }
561 } else
562 m = NULL;
563
564 q = strv_length(m);
565 l = l + q - 1;
566
567 w = realloc(ret, sizeof(char*) * (l+1));
568 if (!w) {
569 ret[k] = NULL;
570 strv_free(ret);
571 strv_free(m);
572 return NULL;
573 }
574
575 ret = w;
576 if (m) {
577 memcpy(ret + k, m, q * sizeof(char*));
578 free(m);
579 }
580
581 k += q;
582 continue;
583 }
584
585 /* If ${FOO} appears as part of a word, replace it by the variable as-is */
586 ret[k] = replace_env(*i, env);
587 if (!ret[k]) {
588 strv_free(ret);
589 return NULL;
590 }
591 k++;
592 }
593
594 ret[k] = NULL;
595 return ret;
596}