]> git.proxmox.com Git - systemd.git/blob - src/basic/env-util.c
Imported Upstream version 227
[systemd.git] / src / basic / env-util.c
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>
23 #include <unistd.h>
24
25 #include "strv.h"
26 #include "utf8.h"
27 #include "util.h"
28 #include "env-util.h"
29 #include "def.h"
30
31 #define VALID_CHARS_ENV_NAME \
32 DIGITS LETTERS \
33 "_"
34
35 #ifndef ARG_MAX
36 #define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX))
37 #endif
38
39 static 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
66 bool 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
73 bool env_value_is_valid(const char *e) {
74 if (!e)
75 return false;
76
77 if (!utf8_is_valid(e))
78 return false;
79
80 /* bash allows tabs in environment variables, and so should
81 * we */
82 if (string_has_cc(e, "\t"))
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
96 bool 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
111 * cannot be either, but let's leave room for one trailing NUL
112 * byte. */
113 if (strlen(e) > ARG_MAX - 1)
114 return false;
115
116 return true;
117 }
118
119 bool 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)
131 if (strneq(*p, *q, k) && (*q)[k] == '=')
132 return false;
133 }
134
135 return true;
136 }
137
138 bool 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
153 static 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++)
174 if (strneq(*j, *a, n))
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
190 char **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
223 fail:
224 va_end(ap);
225 strv_free(r);
226
227 return NULL;
228 }
229
230 _pure_ static bool env_match(const char *t, const char *pattern) {
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
250 return strneq(t, pattern, l) && t[l] == '=';
251 }
252
253 return false;
254 }
255
256 char **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
304 char **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
314 * string list. Edits in-place. */
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 }
325
326 *t = NULL;
327 return l;
328 }
329
330 char **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 }
362
363 *t = NULL;
364 return l;
365 }
366
367 char **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
389 fail:
390 strv_free(r);
391 return NULL;
392 }
393
394 char *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)
403 if (strneq(*i, name, k) &&
404 (*i)[k] == '=')
405 return *i + k + 1;
406
407 return NULL;
408 }
409
410 char *strv_env_get(char **l, const char *name) {
411 assert(name);
412
413 return strv_env_get_n(l, name, strlen(name));
414 }
415
416 char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
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)) {
425 if (invalid_callback)
426 invalid_callback(*p, userdata);
427 free(*p);
428 continue;
429 }
430
431 n = strcspn(*p, "=");
432 STRV_FOREACH(q, p + 1)
433 if (strneq(*p, *q, n) && (*q)[n] == '=') {
434 duplicate = true;
435 break;
436 }
437
438 if (duplicate) {
439 free(*p);
440 continue;
441 }
442
443 e[k++] = *p;
444 }
445
446 if (e)
447 e[k] = NULL;
448
449 return e;
450 }
451
452 char *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
526 fail:
527 free(r);
528 return NULL;
529 }
530
531 char **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 */
544 if ((*i)[0] == '$' && (*i)[1] != '{' && (*i)[1] != '$') {
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
553 r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
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 }