Line data Source code
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 38 : static bool env_name_is_valid_n(const char *e, size_t n) {
40 : const char *p;
41 :
42 38 : if (!e)
43 0 : return false;
44 :
45 38 : if (n <= 0)
46 3 : return false;
47 :
48 35 : if (e[0] >= '0' && e[0] <= '9')
49 2 : 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 33 : if (n > ARG_MAX - 2)
57 0 : return false;
58 :
59 169 : for (p = e; p < e + n; p++)
60 140 : if (!strchr(VALID_CHARS_ENV_NAME, *p))
61 4 : return false;
62 :
63 29 : return true;
64 : }
65 :
66 5 : bool env_name_is_valid(const char *e) {
67 5 : if (!e)
68 1 : return false;
69 :
70 4 : return env_name_is_valid_n(e, strlen(e));
71 : }
72 :
73 28 : bool env_value_is_valid(const char *e) {
74 28 : if (!e)
75 0 : return false;
76 :
77 28 : if (!utf8_is_valid(e))
78 0 : return false;
79 :
80 : /* bash allows tabs in environment variables, and so should
81 : * we */
82 28 : if (string_has_cc(e, "\t"))
83 2 : 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 26 : if (strlen(e) > ARG_MAX - 3)
91 0 : return false;
92 :
93 26 : return true;
94 : }
95 :
96 38 : bool env_assignment_is_valid(const char *e) {
97 : const char *eq;
98 :
99 38 : eq = strchr(e, '=');
100 38 : if (!eq)
101 4 : return false;
102 :
103 34 : if (!env_name_is_valid_n(e, eq - e))
104 6 : return false;
105 :
106 28 : if (!env_value_is_valid(eq + 1))
107 2 : 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 26 : if (strlen(e) > ARG_MAX - 1)
114 0 : return false;
115 :
116 26 : return true;
117 : }
118 :
119 2 : bool strv_env_is_valid(char **e) {
120 : char **p, **q;
121 :
122 7 : STRV_FOREACH(p, e) {
123 : size_t k;
124 :
125 6 : if (!env_assignment_is_valid(*p))
126 0 : return false;
127 :
128 : /* Check if there are duplicate assginments */
129 6 : k = strcspn(*p, "=");
130 16 : STRV_FOREACH(q, p + 1)
131 11 : if (strneq(*p, *q, k) && (*q)[k] == '=')
132 1 : return false;
133 : }
134 :
135 1 : return true;
136 : }
137 :
138 0 : bool strv_env_name_or_assignment_is_valid(char **l) {
139 : char **p, **q;
140 :
141 0 : STRV_FOREACH(p, l) {
142 0 : if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
143 0 : return false;
144 :
145 0 : STRV_FOREACH(q, p + 1)
146 0 : if (streq(*p, *q))
147 0 : return false;
148 : }
149 :
150 0 : return true;
151 : }
152 :
153 16 : static int env_append(char **r, char ***k, char **a) {
154 16 : assert(r);
155 16 : assert(k);
156 :
157 16 : if (!a)
158 0 : 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 252 : for (; *a; a++) {
165 : char **j;
166 : size_t n;
167 :
168 236 : n = strcspn(*a, "=");
169 :
170 236 : if ((*a)[n] == '=')
171 234 : n++;
172 :
173 4257 : for (j = r; j < *k; j++)
174 4025 : if (strneq(*j, *a, n))
175 4 : break;
176 :
177 236 : if (j >= *k)
178 232 : (*k)++;
179 : else
180 4 : free(*j);
181 :
182 236 : *j = strdup(*a);
183 236 : if (!*j)
184 0 : return -ENOMEM;
185 : }
186 :
187 16 : return 0;
188 : }
189 :
190 7 : char **strv_env_merge(unsigned n_lists, ...) {
191 7 : 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 7 : va_start(ap, n_lists);
199 21 : for (i = 0; i < n_lists; i++) {
200 14 : l = va_arg(ap, char**);
201 14 : n += strv_length(l);
202 : }
203 7 : va_end(ap);
204 :
205 7 : r = new(char*, n+1);
206 7 : if (!r)
207 0 : return NULL;
208 :
209 7 : k = r;
210 :
211 7 : va_start(ap, n_lists);
212 21 : for (i = 0; i < n_lists; i++) {
213 14 : l = va_arg(ap, char**);
214 14 : if (env_append(r, &k, l) < 0)
215 0 : goto fail;
216 : }
217 7 : va_end(ap);
218 :
219 7 : *k = NULL;
220 :
221 7 : return r;
222 :
223 : fail:
224 0 : va_end(ap);
225 0 : strv_free(r);
226 :
227 0 : return NULL;
228 : }
229 :
230 2780 : _pure_ static bool env_match(const char *t, const char *pattern) {
231 2780 : assert(t);
232 2780 : 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 2780 : if (streq(t, pattern))
245 1 : return true;
246 :
247 2779 : if (!strchr(pattern, '=')) {
248 2779 : size_t l = strlen(pattern);
249 :
250 2779 : return strneq(t, pattern, l) && t[l] == '=';
251 : }
252 :
253 0 : return false;
254 : }
255 :
256 1 : char **strv_env_delete(char **x, unsigned n_lists, ...) {
257 1 : 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 1 : n = strv_length(x);
265 :
266 1 : r = new(char*, n+1);
267 1 : if (!r)
268 0 : return NULL;
269 :
270 6 : STRV_FOREACH(k, x) {
271 : unsigned v;
272 :
273 5 : va_start(ap, n_lists);
274 10 : for (v = 0; v < n_lists; v++) {
275 : char **l, **j;
276 :
277 8 : l = va_arg(ap, char**);
278 17 : STRV_FOREACH(j, l)
279 12 : if (env_match(*k, *j))
280 3 : goto skip;
281 : }
282 2 : va_end(ap);
283 :
284 2 : r[i] = strdup(*k);
285 2 : if (!r[i]) {
286 0 : strv_free(r);
287 0 : return NULL;
288 : }
289 :
290 2 : i++;
291 2 : continue;
292 :
293 : skip:
294 3 : va_end(ap);
295 : }
296 :
297 1 : r[i] = NULL;
298 :
299 1 : assert(i <= n);
300 :
301 1 : return r;
302 : }
303 :
304 1 : char **strv_env_unset(char **l, const char *p) {
305 :
306 : char **f, **t;
307 :
308 1 : if (!l)
309 0 : return NULL;
310 :
311 1 : assert(p);
312 :
313 : /* Drops every occurrence of the env var setting p in the
314 : * string list. Edits in-place. */
315 :
316 4 : for (f = t = l; *f; f++) {
317 :
318 3 : if (env_match(*f, p)) {
319 1 : free(*f);
320 1 : continue;
321 : }
322 :
323 2 : *(t++) = *f;
324 : }
325 :
326 1 : *t = NULL;
327 1 : return l;
328 : }
329 :
330 11 : char **strv_env_unset_many(char **l, ...) {
331 :
332 : char **f, **t;
333 :
334 11 : if (!l)
335 0 : return NULL;
336 :
337 : /* Like strv_env_unset() but applies many at once. Edits in-place. */
338 :
339 406 : for (f = t = l; *f; f++) {
340 395 : bool found = false;
341 : const char *p;
342 : va_list ap;
343 :
344 395 : va_start(ap, l);
345 :
346 3555 : while ((p = va_arg(ap, const char*))) {
347 2765 : if (env_match(*f, p)) {
348 0 : found = true;
349 0 : break;
350 : }
351 : }
352 :
353 395 : va_end(ap);
354 :
355 395 : if (found) {
356 0 : free(*f);
357 0 : continue;
358 : }
359 :
360 395 : *(t++) = *f;
361 : }
362 :
363 11 : *t = NULL;
364 11 : return l;
365 : }
366 :
367 1 : char **strv_env_set(char **x, const char *p) {
368 :
369 : char **k, **r;
370 1 : char* m[2] = { (char*) p, NULL };
371 :
372 : /* Overrides the env var setting of p, returns a new copy */
373 :
374 1 : r = new(char*, strv_length(x)+2);
375 1 : if (!r)
376 0 : return NULL;
377 :
378 1 : k = r;
379 1 : if (env_append(r, &k, x) < 0)
380 0 : goto fail;
381 :
382 1 : if (env_append(r, &k, m) < 0)
383 0 : goto fail;
384 :
385 1 : *k = NULL;
386 :
387 1 : return r;
388 :
389 : fail:
390 0 : strv_free(r);
391 0 : return NULL;
392 : }
393 :
394 7 : char *strv_env_get_n(char **l, const char *name, size_t k) {
395 : char **i;
396 :
397 7 : assert(name);
398 :
399 7 : if (k <= 0)
400 0 : return NULL;
401 :
402 10 : STRV_FOREACH(i, l)
403 15 : if (strneq(*i, name, k) &&
404 6 : (*i)[k] == '=')
405 6 : return *i + k + 1;
406 :
407 1 : return NULL;
408 : }
409 :
410 2 : char *strv_env_get(char **l, const char *name) {
411 2 : assert(name);
412 :
413 2 : return strv_env_get_n(l, name, strlen(name));
414 : }
415 :
416 3 : char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
417 : char **p, **q;
418 3 : int k = 0;
419 :
420 35 : STRV_FOREACH(p, e) {
421 : size_t n;
422 32 : bool duplicate = false;
423 :
424 32 : if (!env_assignment_is_valid(*p)) {
425 12 : if (invalid_callback)
426 0 : invalid_callback(*p, userdata);
427 12 : free(*p);
428 12 : continue;
429 : }
430 :
431 20 : n = strcspn(*p, "=");
432 108 : STRV_FOREACH(q, p + 1)
433 90 : if (strneq(*p, *q, n) && (*q)[n] == '=') {
434 2 : duplicate = true;
435 2 : break;
436 : }
437 :
438 20 : if (duplicate) {
439 2 : free(*p);
440 2 : continue;
441 : }
442 :
443 18 : e[k++] = *p;
444 : }
445 :
446 3 : if (e)
447 3 : e[k] = NULL;
448 :
449 3 : return e;
450 : }
451 :
452 7 : char *replace_env(const char *format, char **env) {
453 : enum {
454 : WORD,
455 : CURLY,
456 : VARIABLE
457 7 : } state = WORD;
458 :
459 7 : const char *e, *word = format;
460 7 : char *r = NULL, *k;
461 :
462 7 : assert(format);
463 :
464 69 : for (e = format; *e; e ++) {
465 :
466 62 : switch (state) {
467 :
468 : case WORD:
469 30 : if (*e == '$')
470 9 : state = CURLY;
471 30 : break;
472 :
473 : case CURLY:
474 9 : if (*e == '{') {
475 6 : k = strnappend(r, word, e-word-1);
476 6 : if (!k)
477 0 : goto fail;
478 :
479 6 : free(r);
480 6 : r = k;
481 :
482 6 : word = e-1;
483 6 : state = VARIABLE;
484 :
485 3 : } else if (*e == '$') {
486 0 : k = strnappend(r, word, e-word);
487 0 : if (!k)
488 0 : goto fail;
489 :
490 0 : free(r);
491 0 : r = k;
492 :
493 0 : word = e+1;
494 0 : state = WORD;
495 : } else
496 3 : state = WORD;
497 9 : break;
498 :
499 : case VARIABLE:
500 23 : if (*e == '}') {
501 : const char *t;
502 :
503 5 : t = strempty(strv_env_get_n(env, word+2, e-word-2));
504 :
505 5 : k = strappend(r, t);
506 5 : if (!k)
507 0 : goto fail;
508 :
509 5 : free(r);
510 5 : r = k;
511 :
512 5 : word = e+1;
513 5 : state = WORD;
514 : }
515 23 : break;
516 : }
517 : }
518 :
519 7 : k = strnappend(r, word, e-word);
520 7 : if (!k)
521 0 : goto fail;
522 :
523 7 : free(r);
524 7 : return k;
525 :
526 : fail:
527 0 : free(r);
528 0 : return NULL;
529 : }
530 :
531 1 : char **replace_env_argv(char **argv, char **env) {
532 : char **ret, **i;
533 1 : unsigned k = 0, l = 0;
534 :
535 1 : l = strv_length(argv);
536 :
537 1 : ret = new(char*, l+1);
538 1 : if (!ret)
539 0 : return NULL;
540 :
541 10 : STRV_FOREACH(i, argv) {
542 :
543 : /* If $FOO appears as single word, replace it by the split up variable */
544 9 : if ((*i)[0] == '$' && (*i)[1] != '{') {
545 : char *e;
546 2 : char **w, **m = NULL;
547 : unsigned q;
548 :
549 2 : e = strv_env_get(env, *i+1);
550 2 : if (e) {
551 : int r;
552 :
553 1 : r = strv_split_quoted(&m, e, UNQUOTE_RELAX);
554 1 : if (r < 0) {
555 0 : ret[k] = NULL;
556 0 : strv_free(ret);
557 0 : return NULL;
558 : }
559 : } else
560 1 : m = NULL;
561 :
562 2 : q = strv_length(m);
563 2 : l = l + q - 1;
564 :
565 2 : w = realloc(ret, sizeof(char*) * (l+1));
566 2 : if (!w) {
567 0 : ret[k] = NULL;
568 0 : strv_free(ret);
569 0 : strv_free(m);
570 0 : return NULL;
571 : }
572 :
573 2 : ret = w;
574 2 : if (m) {
575 1 : memcpy(ret + k, m, q * sizeof(char*));
576 1 : free(m);
577 : }
578 :
579 2 : k += q;
580 2 : continue;
581 : }
582 :
583 : /* If ${FOO} appears as part of a word, replace it by the variable as-is */
584 7 : ret[k] = replace_env(*i, env);
585 7 : if (!ret[k]) {
586 0 : strv_free(ret);
587 0 : return NULL;
588 : }
589 7 : k++;
590 : }
591 :
592 1 : ret[k] = NULL;
593 1 : return ret;
594 : }
|