chiark / gitweb /
generator: use fflush_and_check() where appropriate
[elogind.git] / src / shared / 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] != '{') {
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_quoted(&m, e, UNQUOTE_RELAX);
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 }