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