chiark / gitweb /
nspawn: allow spawning ephemeral nspawn containers based on the root file system...
[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 <sys/param.h>
24 #include <unistd.h>
25
26 #include "strv.h"
27 #include "utf8.h"
28 #include "util.h"
29 #include "env-util.h"
30 #include "def.h"
31 #include "unit.h"
32
33 #define VALID_CHARS_ENV_NAME                    \
34         DIGITS LETTERS                          \
35         "_"
36
37 #ifndef ARG_MAX
38 #define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX))
39 #endif
40
41 static 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
68 bool 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
75 bool env_value_is_valid(const char *e) {
76         if (!e)
77                 return false;
78
79         if (!utf8_is_valid(e))
80                 return false;
81
82         /* bash allows tabs in environment variables, and so should
83          * we */
84         if (string_has_cc(e, "\t"))
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
98 bool 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
113          * cannot be either, but let's leave room for one trailing NUL
114          * byte. */
115         if (strlen(e) > ARG_MAX - 1)
116                 return false;
117
118         return true;
119 }
120
121 bool 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)
133                         if (strneq(*p, *q, k) && (*q)[k] == '=')
134                                 return false;
135         }
136
137         return true;
138 }
139
140 bool 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
155 static 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++)
176                         if (strneq(*j, *a, n))
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
192 char **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
225 fail:
226         va_end(ap);
227         strv_free(r);
228
229         return NULL;
230 }
231
232 _pure_ static bool env_match(const char *t, const char *pattern) {
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
252                 return strneq(t, pattern, l) && t[l] == '=';
253         }
254
255         return false;
256 }
257
258 char **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
306 char **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
316          * string list. Edits in-place. */
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         }
327
328         *t = NULL;
329         return l;
330 }
331
332 char **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         }
364
365         *t = NULL;
366         return l;
367 }
368
369 char **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
391 fail:
392         strv_free(r);
393         return NULL;
394 }
395
396 char *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)
405                 if (strneq(*i, name, k) &&
406                     (*i)[k] == '=')
407                         return *i + k + 1;
408
409         return NULL;
410 }
411
412 char *strv_env_get(char **l, const char *name) {
413         assert(name);
414
415         return strv_env_get_n(l, name, strlen(name));
416 }
417
418 char **strv_env_clean_log(char **e, const char *unit_id, const char *message) {
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)) {
427                         if (message)
428                                 log_unit_error(unit_id, "Ignoring invalid environment '%s': %s", *p, message);
429                         free(*p);
430                         continue;
431                 }
432
433                 n = strcspn(*p, "=");
434                 STRV_FOREACH(q, p + 1)
435                         if (strneq(*p, *q, n) && (*q)[n] == '=') {
436                                 duplicate = true;
437                                 break;
438                         }
439
440                 if (duplicate) {
441                         free(*p);
442                         continue;
443                 }
444
445                 e[k++] = *p;
446         }
447
448         if (e)
449                 e[k] = NULL;
450
451         return e;
452 }
453
454 char **strv_env_clean(char **e) {
455         return strv_env_clean_log(e, NULL, NULL);
456 }