]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/env-util.c
bootctl: print partition uuids as /dev/disk/by-partuuid/...
[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"
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
41static 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
68bool 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
75bool 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
96bool 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
119bool 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
138static 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
175char **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
208fail:
209 va_end(ap);
210 strv_free(r);
211
212 return NULL;
213}
214
215static 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
241char **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
289char **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
315char **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
337fail:
338 strv_free(r);
339 return NULL;
340}
341
342char *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
358char *strv_env_get(char **l, const char *name) {
359 assert(name);
360
361 return strv_env_get_n(l, name, strlen(name));
362}
363
364char **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}