]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/env-util.c
shared: make timezone and locale enumeration and validation generic
[thirdparty/systemd.git] / src / shared / env-util.c
CommitLineData
4d1a6904
LP
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"
4b549144 30#include "def.h"
4d1a6904
LP
31
32#define VALID_CHARS_ENV_NAME \
4b549144 33 DIGITS LETTERS \
4d1a6904
LP
34 "_"
35
36#ifndef ARG_MAX
37#define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX))
38#endif
39
40static bool env_name_is_valid_n(const char *e, size_t n) {
41 const char *p;
42
43 if (!e)
44 return false;
45
46 if (n <= 0)
47 return false;
48
49 if (e[0] >= '0' && e[0] <= '9')
50 return false;
51
52 /* POSIX says the overall size of the environment block cannot
53 * be > ARG_MAX, an individual assignment hence cannot be
54 * either. Discounting the equal sign and trailing NUL this
55 * hence leaves ARG_MAX-2 as longest possible variable
56 * name. */
57 if (n > ARG_MAX - 2)
58 return false;
59
60 for (p = e; p < e + n; p++)
61 if (!strchr(VALID_CHARS_ENV_NAME, *p))
62 return false;
63
64 return true;
65}
66
67bool env_name_is_valid(const char *e) {
68 if (!e)
69 return false;
70
71 return env_name_is_valid_n(e, strlen(e));
72}
73
74bool env_value_is_valid(const char *e) {
75 if (!e)
76 return false;
77
78 if (!utf8_is_valid(e))
79 return false;
80
81 if (string_has_cc(e))
82 return false;
83
84 /* POSIX says the overall size of the environment block cannot
85 * be > ARG_MAX, an individual assignment hence cannot be
86 * either. Discounting the shortest possible variable name of
87 * length 1, the equal sign and trailing NUL this hence leaves
88 * ARG_MAX-3 as longest possible variable value. */
89 if (strlen(e) > ARG_MAX - 3)
90 return false;
91
92 return true;
93}
94
95bool env_assignment_is_valid(const char *e) {
96 const char *eq;
97
98 eq = strchr(e, '=');
99 if (!eq)
100 return false;
101
102 if (!env_name_is_valid_n(e, eq - e))
103 return false;
104
105 if (!env_value_is_valid(eq + 1))
106 return false;
107
108 /* POSIX says the overall size of the environment block cannot
109 * be > ARG_MAX, hence the individual variable assignments
5f9cfd4c 110 * cannot be either, but let's leave room for one trailing NUL
4d1a6904
LP
111 * byte. */
112 if (strlen(e) > ARG_MAX - 1)
113 return false;
114
115 return true;
116}
117
118bool strv_env_is_valid(char **e) {
119 char **p, **q;
120
121 STRV_FOREACH(p, e) {
122 size_t k;
123
124 if (!env_assignment_is_valid(*p))
125 return false;
126
127 /* Check if there are duplicate assginments */
128 k = strcspn(*p, "=");
129 STRV_FOREACH(q, p + 1)
641906e9 130 if (strneq(*p, *q, k) && (*q)[k] == '=')
4d1a6904
LP
131 return false;
132 }
133
134 return true;
135}
136
123b964a
LP
137bool strv_env_name_or_assignment_is_valid(char **l) {
138 char **p, **q;
139
140 STRV_FOREACH(p, l) {
141 if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
142 return false;
143
144 STRV_FOREACH(q, p + 1)
145 if (streq(*p, *q))
146 return false;
147 }
148
149 return true;
150}
151
4d1a6904
LP
152static int env_append(char **r, char ***k, char **a) {
153 assert(r);
154 assert(k);
155
156 if (!a)
157 return 0;
158
159 /* Add the entries of a to *k unless they already exist in *r
160 * in which case they are overridden instead. This assumes
161 * there is enough space in the r array. */
162
163 for (; *a; a++) {
164 char **j;
165 size_t n;
166
167 n = strcspn(*a, "=");
168
169 if ((*a)[n] == '=')
170 n++;
171
172 for (j = r; j < *k; j++)
641906e9 173 if (strneq(*j, *a, n))
4d1a6904
LP
174 break;
175
176 if (j >= *k)
177 (*k)++;
178 else
179 free(*j);
180
181 *j = strdup(*a);
182 if (!*j)
183 return -ENOMEM;
184 }
185
186 return 0;
187}
188
189char **strv_env_merge(unsigned n_lists, ...) {
190 size_t n = 0;
191 char **l, **k, **r;
192 va_list ap;
193 unsigned i;
194
195 /* Merges an arbitrary number of environment sets */
196
197 va_start(ap, n_lists);
198 for (i = 0; i < n_lists; i++) {
199 l = va_arg(ap, char**);
200 n += strv_length(l);
201 }
202 va_end(ap);
203
204 r = new(char*, n+1);
205 if (!r)
206 return NULL;
207
208 k = r;
209
210 va_start(ap, n_lists);
211 for (i = 0; i < n_lists; i++) {
212 l = va_arg(ap, char**);
213 if (env_append(r, &k, l) < 0)
214 goto fail;
215 }
216 va_end(ap);
217
218 *k = NULL;
219
220 return r;
221
222fail:
223 va_end(ap);
224 strv_free(r);
225
226 return NULL;
227}
228
44a6b1b6 229_pure_ static bool env_match(const char *t, const char *pattern) {
4d1a6904
LP
230 assert(t);
231 assert(pattern);
232
233 /* pattern a matches string a
234 * a matches a=
235 * a matches a=b
236 * a= matches a=
237 * a=b matches a=b
238 * a= does not match a
239 * a=b does not match a=
240 * a=b does not match a
241 * a=b does not match a=c */
242
243 if (streq(t, pattern))
244 return true;
245
246 if (!strchr(pattern, '=')) {
247 size_t l = strlen(pattern);
248
641906e9 249 return strneq(t, pattern, l) && t[l] == '=';
4d1a6904
LP
250 }
251
252 return false;
253}
254
255char **strv_env_delete(char **x, unsigned n_lists, ...) {
256 size_t n, i = 0;
257 char **k, **r;
258 va_list ap;
259
260 /* Deletes every entry from x that is mentioned in the other
261 * string lists */
262
263 n = strv_length(x);
264
265 r = new(char*, n+1);
266 if (!r)
267 return NULL;
268
269 STRV_FOREACH(k, x) {
270 unsigned v;
271
272 va_start(ap, n_lists);
273 for (v = 0; v < n_lists; v++) {
274 char **l, **j;
275
276 l = va_arg(ap, char**);
277 STRV_FOREACH(j, l)
278 if (env_match(*k, *j))
279 goto skip;
280 }
281 va_end(ap);
282
283 r[i] = strdup(*k);
284 if (!r[i]) {
285 strv_free(r);
286 return NULL;
287 }
288
289 i++;
290 continue;
291
292 skip:
293 va_end(ap);
294 }
295
296 r[i] = NULL;
297
298 assert(i <= n);
299
300 return r;
301}
302
303char **strv_env_unset(char **l, const char *p) {
304
305 char **f, **t;
306
307 if (!l)
308 return NULL;
309
310 assert(p);
311
312 /* Drops every occurrence of the env var setting p in the
43d03a83 313 * string list. Edits in-place. */
4d1a6904
LP
314
315 for (f = t = l; *f; f++) {
316
317 if (env_match(*f, p)) {
318 free(*f);
319 continue;
320 }
321
322 *(t++) = *f;
323 }
43d03a83
LP
324
325 *t = NULL;
326 return l;
327}
328
329char **strv_env_unset_many(char **l, ...) {
330
331 char **f, **t;
332
333 if (!l)
334 return NULL;
335
336 /* Like strv_env_unset() but applies many at once. Edits in-place. */
337
338 for (f = t = l; *f; f++) {
339 bool found = false;
340 const char *p;
341 va_list ap;
342
343 va_start(ap, l);
344
345 while ((p = va_arg(ap, const char*))) {
346 if (env_match(*f, p)) {
347 found = true;
348 break;
349 }
350 }
351
352 va_end(ap);
353
354 if (found) {
355 free(*f);
356 continue;
357 }
358
359 *(t++) = *f;
360 }
4d1a6904
LP
361
362 *t = NULL;
363 return l;
364}
365
366char **strv_env_set(char **x, const char *p) {
367
368 char **k, **r;
369 char* m[2] = { (char*) p, NULL };
370
371 /* Overrides the env var setting of p, returns a new copy */
372
373 r = new(char*, strv_length(x)+2);
374 if (!r)
375 return NULL;
376
377 k = r;
378 if (env_append(r, &k, x) < 0)
379 goto fail;
380
381 if (env_append(r, &k, m) < 0)
382 goto fail;
383
384 *k = NULL;
385
386 return r;
387
388fail:
389 strv_free(r);
390 return NULL;
391}
392
393char *strv_env_get_n(char **l, const char *name, size_t k) {
394 char **i;
395
396 assert(name);
397
398 if (k <= 0)
399 return NULL;
400
401 STRV_FOREACH(i, l)
641906e9 402 if (strneq(*i, name, k) &&
4d1a6904
LP
403 (*i)[k] == '=')
404 return *i + k + 1;
405
406 return NULL;
407}
408
409char *strv_env_get(char **l, const char *name) {
410 assert(name);
411
412 return strv_env_get_n(l, name, strlen(name));
413}
414
ebc05a09 415char **strv_env_clean_log(char **e, const char *message) {
4d1a6904
LP
416 char **p, **q;
417 int k = 0;
418
419 STRV_FOREACH(p, e) {
420 size_t n;
421 bool duplicate = false;
422
423 if (!env_assignment_is_valid(*p)) {
ebc05a09
HH
424 if (message)
425 log_error("Ignoring invalid environment '%s': %s", *p, message);
4d1a6904
LP
426 free(*p);
427 continue;
428 }
429
430 n = strcspn(*p, "=");
431 STRV_FOREACH(q, p + 1)
641906e9 432 if (strneq(*p, *q, n) && (*q)[n] == '=') {
4d1a6904
LP
433 duplicate = true;
434 break;
435 }
436
437 if (duplicate) {
438 free(*p);
439 continue;
440 }
441
442 e[k++] = *p;
443 }
444
5b4fb02d
LP
445 if (e)
446 e[k] = NULL;
447
4d1a6904
LP
448 return e;
449}
ebc05a09
HH
450
451char **strv_env_clean(char **e) {
452 return strv_env_clean_log(e, NULL);
453}