chiark / gitweb /
use strneq instead of strncmp
[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 (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 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_set(char **x, const char *p) {
331
332         char **k, **r;
333         char* m[2] = { (char*) p, NULL };
334
335         /* Overrides the env var setting of p, returns a new copy */
336
337         r = new(char*, strv_length(x)+2);
338         if (!r)
339                 return NULL;
340
341         k = r;
342         if (env_append(r, &k, x) < 0)
343                 goto fail;
344
345         if (env_append(r, &k, m) < 0)
346                 goto fail;
347
348         *k = NULL;
349
350         return r;
351
352 fail:
353         strv_free(r);
354         return NULL;
355 }
356
357 char *strv_env_get_n(char **l, const char *name, size_t k) {
358         char **i;
359
360         assert(name);
361
362         if (k <= 0)
363                 return NULL;
364
365         STRV_FOREACH(i, l)
366                 if (strneq(*i, name, k) &&
367                     (*i)[k] == '=')
368                         return *i + k + 1;
369
370         return NULL;
371 }
372
373 char *strv_env_get(char **l, const char *name) {
374         assert(name);
375
376         return strv_env_get_n(l, name, strlen(name));
377 }
378
379 char **strv_env_clean(char **e) {
380         char **p, **q;
381         int k = 0;
382
383         STRV_FOREACH(p, e) {
384                 size_t n;
385                 bool duplicate = false;
386
387                 if (!env_assignment_is_valid(*p)) {
388                         free(*p);
389                         continue;
390                 }
391
392                 n = strcspn(*p, "=");
393                 STRV_FOREACH(q, p + 1)
394                         if (strneq(*p, *q, n) && (*q)[n] == '=') {
395                                 duplicate = true;
396                                 break;
397                         }
398
399                 if (duplicate) {
400                         free(*p);
401                         continue;
402                 }
403
404                 e[k++] = *p;
405         }
406
407         e[k] = NULL;
408         return e;
409 }