]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/env-util.c
efi: never call qsort on potentially NULL arrays
[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
313 * string list. edits in-place. */
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 }
324
325 *t = NULL;
326 return l;
327}
328
329char **strv_env_set(char **x, const char *p) {
330
331 char **k, **r;
332 char* m[2] = { (char*) p, NULL };
333
334 /* Overrides the env var setting of p, returns a new copy */
335
336 r = new(char*, strv_length(x)+2);
337 if (!r)
338 return NULL;
339
340 k = r;
341 if (env_append(r, &k, x) < 0)
342 goto fail;
343
344 if (env_append(r, &k, m) < 0)
345 goto fail;
346
347 *k = NULL;
348
349 return r;
350
351fail:
352 strv_free(r);
353 return NULL;
354}
355
356char *strv_env_get_n(char **l, const char *name, size_t k) {
357 char **i;
358
359 assert(name);
360
361 if (k <= 0)
362 return NULL;
363
364 STRV_FOREACH(i, l)
641906e9 365 if (strneq(*i, name, k) &&
4d1a6904
LP
366 (*i)[k] == '=')
367 return *i + k + 1;
368
369 return NULL;
370}
371
372char *strv_env_get(char **l, const char *name) {
373 assert(name);
374
375 return strv_env_get_n(l, name, strlen(name));
376}
377
ebc05a09 378char **strv_env_clean_log(char **e, const char *message) {
4d1a6904
LP
379 char **p, **q;
380 int k = 0;
381
382 STRV_FOREACH(p, e) {
383 size_t n;
384 bool duplicate = false;
385
386 if (!env_assignment_is_valid(*p)) {
ebc05a09
HH
387 if (message)
388 log_error("Ignoring invalid environment '%s': %s", *p, message);
4d1a6904
LP
389 free(*p);
390 continue;
391 }
392
393 n = strcspn(*p, "=");
394 STRV_FOREACH(q, p + 1)
641906e9 395 if (strneq(*p, *q, n) && (*q)[n] == '=') {
4d1a6904
LP
396 duplicate = true;
397 break;
398 }
399
400 if (duplicate) {
401 free(*p);
402 continue;
403 }
404
405 e[k++] = *p;
406 }
407
408 e[k] = NULL;
409 return e;
410}
ebc05a09
HH
411
412char **strv_env_clean(char **e) {
413 return strv_env_clean_log(e, NULL);
414}