chiark / gitweb /
7a213a77c0ea8ea9261045c65e6f015960a20040
[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
31 #define VALID_CHARS_ENV_NAME                    \
32         "0123456789"                            \
33         "abcdefghijklmnopqrstuvwxyz"            \
34         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"            \
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         if (string_has_cc(e))
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 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 (strncmp(*p, *q, k) == 0 && (*q)[k] == '=')
132                                 return false;
133         }
134
135         return true;
136 }
137
138 static int env_append(char **r, char ***k, char **a) {
139         assert(r);
140         assert(k);
141
142         if (!a)
143                 return 0;
144
145         /* Add the entries of a to *k unless they already exist in *r
146          * in which case they are overridden instead. This assumes
147          * there is enough space in the r array. */
148
149         for (; *a; a++) {
150                 char **j;
151                 size_t n;
152
153                 n = strcspn(*a, "=");
154
155                 if ((*a)[n] == '=')
156                         n++;
157
158                 for (j = r; j < *k; j++)
159                         if (strncmp(*j, *a, n) == 0)
160                                 break;
161
162                 if (j >= *k)
163                         (*k)++;
164                 else
165                         free(*j);
166
167                 *j = strdup(*a);
168                 if (!*j)
169                         return -ENOMEM;
170         }
171
172         return 0;
173 }
174
175 char **strv_env_merge(unsigned n_lists, ...) {
176         size_t n = 0;
177         char **l, **k, **r;
178         va_list ap;
179         unsigned i;
180
181         /* Merges an arbitrary number of environment sets */
182
183         va_start(ap, n_lists);
184         for (i = 0; i < n_lists; i++) {
185                 l = va_arg(ap, char**);
186                 n += strv_length(l);
187         }
188         va_end(ap);
189
190         r = new(char*, n+1);
191         if (!r)
192                 return NULL;
193
194         k = r;
195
196         va_start(ap, n_lists);
197         for (i = 0; i < n_lists; i++) {
198                 l = va_arg(ap, char**);
199                 if (env_append(r, &k, l) < 0)
200                         goto fail;
201         }
202         va_end(ap);
203
204         *k = NULL;
205
206         return r;
207
208 fail:
209         va_end(ap);
210         strv_free(r);
211
212         return NULL;
213 }
214
215 static bool env_match(const char *t, const char *pattern) {
216         assert(t);
217         assert(pattern);
218
219         /* pattern a matches string a
220          *         a matches a=
221          *         a matches a=b
222          *         a= matches a=
223          *         a=b matches a=b
224          *         a= does not match a
225          *         a=b does not match a=
226          *         a=b does not match a
227          *         a=b does not match a=c */
228
229         if (streq(t, pattern))
230                 return true;
231
232         if (!strchr(pattern, '=')) {
233                 size_t l = strlen(pattern);
234
235                 return strncmp(t, pattern, l) == 0 && t[l] == '=';
236         }
237
238         return false;
239 }
240
241 char **strv_env_delete(char **x, unsigned n_lists, ...) {
242         size_t n, i = 0;
243         char **k, **r;
244         va_list ap;
245
246         /* Deletes every entry from x that is mentioned in the other
247          * string lists */
248
249         n = strv_length(x);
250
251         r = new(char*, n+1);
252         if (!r)
253                 return NULL;
254
255         STRV_FOREACH(k, x) {
256                 unsigned v;
257
258                 va_start(ap, n_lists);
259                 for (v = 0; v < n_lists; v++) {
260                         char **l, **j;
261
262                         l = va_arg(ap, char**);
263                         STRV_FOREACH(j, l)
264                                 if (env_match(*k, *j))
265                                         goto skip;
266                 }
267                 va_end(ap);
268
269                 r[i] = strdup(*k);
270                 if (!r[i]) {
271                         strv_free(r);
272                         return NULL;
273                 }
274
275                 i++;
276                 continue;
277
278         skip:
279                 va_end(ap);
280         }
281
282         r[i] = NULL;
283
284         assert(i <= n);
285
286         return r;
287 }
288
289 char **strv_env_unset(char **l, const char *p) {
290
291         char **f, **t;
292
293         if (!l)
294                 return NULL;
295
296         assert(p);
297
298         /* Drops every occurrence of the env var setting p in the
299          * string list. edits in-place. */
300
301         for (f = t = l; *f; f++) {
302
303                 if (env_match(*f, p)) {
304                         free(*f);
305                         continue;
306                 }
307
308                 *(t++) = *f;
309         }
310
311         *t = NULL;
312         return l;
313 }
314
315 char **strv_env_set(char **x, const char *p) {
316
317         char **k, **r;
318         char* m[2] = { (char*) p, NULL };
319
320         /* Overrides the env var setting of p, returns a new copy */
321
322         r = new(char*, strv_length(x)+2);
323         if (!r)
324                 return NULL;
325
326         k = r;
327         if (env_append(r, &k, x) < 0)
328                 goto fail;
329
330         if (env_append(r, &k, m) < 0)
331                 goto fail;
332
333         *k = NULL;
334
335         return r;
336
337 fail:
338         strv_free(r);
339         return NULL;
340 }
341
342 char *strv_env_get_n(char **l, const char *name, size_t k) {
343         char **i;
344
345         assert(name);
346
347         if (k <= 0)
348                 return NULL;
349
350         STRV_FOREACH(i, l)
351                 if (strncmp(*i, name, k) == 0 &&
352                     (*i)[k] == '=')
353                         return *i + k + 1;
354
355         return NULL;
356 }
357
358 char *strv_env_get(char **l, const char *name) {
359         assert(name);
360
361         return strv_env_get_n(l, name, strlen(name));
362 }
363
364 char **strv_env_clean(char **e) {
365         char **p, **q;
366         int k = 0;
367
368         STRV_FOREACH(p, e) {
369                 size_t n;
370                 bool duplicate = false;
371
372                 if (!env_assignment_is_valid(*p)) {
373                         free(*p);
374                         continue;
375                 }
376
377                 n = strcspn(*p, "=");
378                 STRV_FOREACH(q, p + 1)
379                         if (strncmp(*p, *q, n) == 0 && (*q)[n] == '=') {
380                                 duplicate = true;
381                                 break;
382                         }
383
384                 if (duplicate) {
385                         free(*p);
386                         continue;
387                 }
388
389                 e[k++] = *p;
390         }
391
392         e[k] = NULL;
393         return e;
394 }