]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
c24eb49e | 2 | /*** |
b8c83cfc | 3 | Copyright 2016 Zbigniew Jędrzejewski-Szmek |
c24eb49e LP |
4 | ***/ |
5 | ||
c24eb49e LP |
6 | #include <string.h> |
7 | ||
4d1a6904 | 8 | #include "env-util.h" |
a096d8c8 ZJS |
9 | #include "fd-util.h" |
10 | #include "fileio.h" | |
07630cea LP |
11 | #include "string-util.h" |
12 | #include "strv.h" | |
13 | #include "util.h" | |
c24eb49e | 14 | |
940bd473 TA |
15 | static void test_strv_env_delete(void) { |
16 | _cleanup_strv_free_ char **a = NULL, **b = NULL, **c = NULL, **d = NULL; | |
17 | ||
18 | a = strv_new("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF", NULL); | |
9b5d6bd9 LP |
19 | assert_se(a); |
20 | ||
940bd473 | 21 | b = strv_new("PIEP", "FOO", NULL); |
9b5d6bd9 LP |
22 | assert_se(b); |
23 | ||
940bd473 | 24 | c = strv_new("SCHLUMPF", NULL); |
9b5d6bd9 | 25 | assert_se(c); |
940bd473 TA |
26 | |
27 | d = strv_env_delete(a, 2, b, c); | |
9b5d6bd9 | 28 | assert_se(d); |
940bd473 | 29 | |
9b5d6bd9 LP |
30 | assert_se(streq(d[0], "WALDO=WALDO")); |
31 | assert_se(streq(d[1], "WALDO=")); | |
32 | assert_se(strv_length(d) == 2); | |
940bd473 TA |
33 | } |
34 | ||
6162512c RS |
35 | static void test_strv_env_get(void) { |
36 | char **l; | |
37 | ||
38 | l = STRV_MAKE("ONE_OR_TWO=1", "THREE=3", "ONE_OR_TWO=2", "FOUR=4"); | |
39 | ||
40 | assert_se(streq(strv_env_get(l, "ONE_OR_TWO"), "2")); | |
41 | assert_se(streq(strv_env_get(l, "THREE"), "3")); | |
42 | assert_se(streq(strv_env_get(l, "FOUR"), "4")); | |
43 | } | |
44 | ||
940bd473 TA |
45 | static void test_strv_env_unset(void) { |
46 | _cleanup_strv_free_ char **l = NULL; | |
47 | ||
48 | l = strv_new("PIEP", "SCHLUMPF=SMURFF", "NANANANA=YES", NULL); | |
9b5d6bd9 | 49 | assert_se(l); |
940bd473 | 50 | |
9b5d6bd9 | 51 | assert_se(strv_env_unset(l, "SCHLUMPF") == l); |
940bd473 | 52 | |
9b5d6bd9 LP |
53 | assert_se(streq(l[0], "PIEP")); |
54 | assert_se(streq(l[1], "NANANANA=YES")); | |
55 | assert_se(strv_length(l) == 2); | |
940bd473 TA |
56 | } |
57 | ||
58 | static void test_strv_env_set(void) { | |
59 | _cleanup_strv_free_ char **l = NULL, **r = NULL; | |
60 | ||
61 | l = strv_new("PIEP", "SCHLUMPF=SMURFF", "NANANANA=YES", NULL); | |
9b5d6bd9 | 62 | assert_se(l); |
940bd473 TA |
63 | |
64 | r = strv_env_set(l, "WALDO=WALDO"); | |
9b5d6bd9 | 65 | assert_se(r); |
940bd473 | 66 | |
9b5d6bd9 LP |
67 | assert_se(streq(r[0], "PIEP")); |
68 | assert_se(streq(r[1], "SCHLUMPF=SMURFF")); | |
69 | assert_se(streq(r[2], "NANANANA=YES")); | |
70 | assert_se(streq(r[3], "WALDO=WALDO")); | |
71 | assert_se(strv_length(r) == 4); | |
940bd473 TA |
72 | } |
73 | ||
1f28b2de TA |
74 | static void test_strv_env_merge(void) { |
75 | _cleanup_strv_free_ char **a = NULL, **b = NULL, **r = NULL; | |
76 | ||
77 | a = strv_new("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF", NULL); | |
9b5d6bd9 LP |
78 | assert_se(a); |
79 | ||
1f28b2de | 80 | b = strv_new("FOO=KKK", "FOO=", "PIEP=", "SCHLUMPF=SMURFF", "NANANANA=YES", NULL); |
9b5d6bd9 | 81 | assert_se(b); |
c24eb49e | 82 | |
1f28b2de | 83 | r = strv_env_merge(2, a, b); |
9b5d6bd9 LP |
84 | assert_se(r); |
85 | assert_se(streq(r[0], "FOO=")); | |
86 | assert_se(streq(r[1], "WALDO=")); | |
87 | assert_se(streq(r[2], "PIEP")); | |
88 | assert_se(streq(r[3], "SCHLUMPF=SMURFF")); | |
89 | assert_se(streq(r[4], "PIEP=")); | |
90 | assert_se(streq(r[5], "NANANANA=YES")); | |
91 | assert_se(strv_length(r) == 6); | |
92 | ||
93 | assert_se(strv_env_clean(r) == r); | |
94 | assert_se(streq(r[0], "FOO=")); | |
95 | assert_se(streq(r[1], "WALDO=")); | |
96 | assert_se(streq(r[2], "SCHLUMPF=SMURFF")); | |
97 | assert_se(streq(r[3], "PIEP=")); | |
98 | assert_se(streq(r[4], "NANANANA=YES")); | |
99 | assert_se(strv_length(r) == 5); | |
1f28b2de TA |
100 | } |
101 | ||
37f3ffca RS |
102 | static void test_env_strv_get_n(void) { |
103 | const char *_env[] = { | |
104 | "FOO=NO NO NO", | |
105 | "FOO=BAR BAR", | |
106 | "BAR=waldo", | |
107 | "PATH=unset", | |
108 | NULL | |
109 | }; | |
110 | char **env = (char**) _env; | |
111 | ||
112 | assert_se(streq(strv_env_get_n(env, "FOO__", 3, 0), "BAR BAR")); | |
113 | assert_se(streq(strv_env_get_n(env, "FOO__", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR")); | |
114 | assert_se(streq(strv_env_get_n(env, "FOO", 3, 0), "BAR BAR")); | |
115 | assert_se(streq(strv_env_get_n(env, "FOO", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR")); | |
116 | ||
117 | assert_se(streq(strv_env_get_n(env, "PATH__", 4, 0), "unset")); | |
118 | assert_se(streq(strv_env_get_n(env, "PATH", 4, 0), "unset")); | |
119 | assert_se(streq(strv_env_get_n(env, "PATH__", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset")); | |
120 | assert_se(streq(strv_env_get_n(env, "PATH", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset")); | |
121 | ||
122 | env[3] = NULL; /* kill our $PATH */ | |
123 | ||
124 | assert_se(!strv_env_get_n(env, "PATH__", 4, 0)); | |
125 | assert_se(!strv_env_get_n(env, "PATH", 4, 0)); | |
126 | assert_se(streq(strv_env_get_n(env, "PATH__", 4, REPLACE_ENV_USE_ENVIRONMENT), | |
127 | getenv("PATH"))); | |
128 | assert_se(streq(strv_env_get_n(env, "PATH", 4, REPLACE_ENV_USE_ENVIRONMENT), | |
129 | getenv("PATH"))); | |
130 | } | |
131 | ||
ccad1fd0 ZJS |
132 | static void test_replace_env(bool braceless) { |
133 | const char *env[] = { | |
134 | "FOO=BAR BAR", | |
135 | "BAR=waldo", | |
136 | NULL | |
137 | }; | |
138 | _cleanup_free_ char *t = NULL, *s = NULL, *q = NULL, *r = NULL, *p = NULL; | |
139 | unsigned flags = REPLACE_ENV_ALLOW_BRACELESS*braceless; | |
140 | ||
141 | t = replace_env("FOO=$FOO=${FOO}", (char**) env, flags); | |
142 | assert_se(streq(t, braceless ? "FOO=BAR BAR=BAR BAR" : "FOO=$FOO=BAR BAR")); | |
143 | ||
144 | s = replace_env("BAR=$BAR=${BAR}", (char**) env, flags); | |
145 | assert_se(streq(s, braceless ? "BAR=waldo=waldo" : "BAR=$BAR=waldo")); | |
146 | ||
147 | q = replace_env("BARBAR=$BARBAR=${BARBAR}", (char**) env, flags); | |
148 | assert_se(streq(q, braceless ? "BARBAR==" : "BARBAR=$BARBAR=")); | |
149 | ||
a93236aa ZJS |
150 | r = replace_env("BAR=$BAR$BAR${BAR}${BAR}", (char**) env, flags); |
151 | assert_se(streq(r, braceless ? "BAR=waldowaldowaldowaldo" : "BAR=$BAR$BARwaldowaldo")); | |
ccad1fd0 ZJS |
152 | |
153 | p = replace_env("${BAR}$BAR$BAR", (char**) env, flags); | |
154 | assert_se(streq(p, braceless ? "waldowaldowaldo" : "waldo$BAR$BAR")); | |
155 | } | |
156 | ||
f50ce8fc ZJS |
157 | static void test_replace_env2(bool extended) { |
158 | const char *env[] = { | |
159 | "FOO=foo", | |
160 | "BAR=bar", | |
161 | NULL | |
162 | }; | |
163 | _cleanup_free_ char *t = NULL, *s = NULL, *q = NULL, *r = NULL, *p = NULL, *x = NULL; | |
164 | unsigned flags = REPLACE_ENV_ALLOW_EXTENDED*extended; | |
165 | ||
166 | t = replace_env("FOO=${FOO:-${BAR}}", (char**) env, flags); | |
167 | assert_se(streq(t, extended ? "FOO=foo" : "FOO=${FOO:-bar}")); | |
168 | ||
169 | s = replace_env("BAR=${XXX:-${BAR}}", (char**) env, flags); | |
170 | assert_se(streq(s, extended ? "BAR=bar" : "BAR=${XXX:-bar}")); | |
171 | ||
172 | q = replace_env("XXX=${XXX:+${BAR}}", (char**) env, flags); | |
173 | assert_se(streq(q, extended ? "XXX=" : "XXX=${XXX:+bar}")); | |
174 | ||
175 | r = replace_env("FOO=${FOO:+${BAR}}", (char**) env, flags); | |
176 | assert_se(streq(r, extended ? "FOO=bar" : "FOO=${FOO:+bar}")); | |
177 | ||
178 | p = replace_env("FOO=${FOO:-${BAR}post}", (char**) env, flags); | |
179 | assert_se(streq(p, extended ? "FOO=foo" : "FOO=${FOO:-barpost}")); | |
180 | ||
181 | x = replace_env("XXX=${XXX:+${BAR}post}", (char**) env, flags); | |
182 | assert_se(streq(x, extended ? "XXX=" : "XXX=${XXX:+barpost}")); | |
183 | } | |
184 | ||
ccad1fd0 | 185 | static void test_replace_env_argv(void) { |
c24eb49e LP |
186 | const char *env[] = { |
187 | "FOO=BAR BAR", | |
188 | "BAR=waldo", | |
189 | NULL | |
190 | }; | |
c24eb49e LP |
191 | const char *line[] = { |
192 | "FOO$FOO", | |
193 | "FOO$FOOFOO", | |
194 | "FOO${FOO}$FOO", | |
195 | "FOO${FOO}", | |
196 | "${FOO}", | |
197 | "$FOO", | |
198 | "$FOO$FOO", | |
199 | "${FOO}${BAR}", | |
200 | "${FOO", | |
df553b58 MS |
201 | "FOO$$${FOO}", |
202 | "$$FOO${FOO}", | |
b82f58bf RS |
203 | "${FOO:-${BAR}}", |
204 | "${QUUX:-${FOO}}", | |
205 | "${FOO:+${BAR}}", | |
206 | "${QUUX:+${BAR}}", | |
207 | "${FOO:+|${BAR}|}}", | |
208 | "${FOO:+|${BAR}{|}", | |
c24eb49e LP |
209 | NULL |
210 | }; | |
1f28b2de | 211 | _cleanup_strv_free_ char **r = NULL; |
c24eb49e LP |
212 | |
213 | r = replace_env_argv((char**) line, (char**) env); | |
9b5d6bd9 LP |
214 | assert_se(r); |
215 | assert_se(streq(r[0], "FOO$FOO")); | |
216 | assert_se(streq(r[1], "FOO$FOOFOO")); | |
217 | assert_se(streq(r[2], "FOOBAR BAR$FOO")); | |
218 | assert_se(streq(r[3], "FOOBAR BAR")); | |
219 | assert_se(streq(r[4], "BAR BAR")); | |
220 | assert_se(streq(r[5], "BAR")); | |
221 | assert_se(streq(r[6], "BAR")); | |
222 | assert_se(streq(r[7], "BAR BARwaldo")); | |
223 | assert_se(streq(r[8], "${FOO")); | |
df553b58 MS |
224 | assert_se(streq(r[9], "FOO$BAR BAR")); |
225 | assert_se(streq(r[10], "$FOOBAR BAR")); | |
b82f58bf RS |
226 | assert_se(streq(r[11], "${FOO:-waldo}")); |
227 | assert_se(streq(r[12], "${QUUX:-BAR BAR}")); | |
228 | assert_se(streq(r[13], "${FOO:+waldo}")); | |
229 | assert_se(streq(r[14], "${QUUX:+waldo}")); | |
230 | assert_se(streq(r[15], "${FOO:+|waldo|}}")); | |
231 | assert_se(streq(r[16], "${FOO:+|waldo{|}")); | |
232 | assert_se(strv_length(r) == 17); | |
1f28b2de | 233 | } |
a6ff950e | 234 | |
4d1a6904 | 235 | static void test_env_clean(void) { |
4d1a6904 LP |
236 | _cleanup_strv_free_ char **e; |
237 | ||
238 | e = strv_new("FOOBAR=WALDO", | |
239 | "FOOBAR=WALDO", | |
240 | "FOOBAR", | |
241 | "F", | |
242 | "X=", | |
243 | "F=F", | |
244 | "=", | |
245 | "=F", | |
246 | "", | |
247 | "0000=000", | |
248 | "äöüß=abcd", | |
249 | "abcd=äöüß", | |
250 | "xyz\n=xyz", | |
251 | "xyz=xyz\n", | |
252 | "another=one", | |
253 | "another=final one", | |
254 | NULL); | |
9b5d6bd9 LP |
255 | assert_se(e); |
256 | assert_se(!strv_env_is_valid(e)); | |
257 | assert_se(strv_env_clean(e) == e); | |
258 | assert_se(strv_env_is_valid(e)); | |
4d1a6904 LP |
259 | |
260 | assert_se(streq(e[0], "FOOBAR=WALDO")); | |
261 | assert_se(streq(e[1], "X=")); | |
262 | assert_se(streq(e[2], "F=F")); | |
263 | assert_se(streq(e[3], "abcd=äöüß")); | |
b4346b9a FB |
264 | assert_se(streq(e[4], "xyz=xyz\n")); |
265 | assert_se(streq(e[5], "another=final one")); | |
266 | assert_se(e[6] == NULL); | |
4d1a6904 LP |
267 | } |
268 | ||
8354c34e TA |
269 | static void test_env_name_is_valid(void) { |
270 | assert_se(env_name_is_valid("test")); | |
271 | ||
272 | assert_se(!env_name_is_valid(NULL)); | |
273 | assert_se(!env_name_is_valid("")); | |
b8c83cfc ZJS |
274 | assert_se(!env_name_is_valid("xxx\a")); |
275 | assert_se(!env_name_is_valid("xxx\007b")); | |
276 | assert_se(!env_name_is_valid("\007\009")); | |
8354c34e TA |
277 | assert_se(!env_name_is_valid("5_starting_with_a_number_is_wrong")); |
278 | assert_se(!env_name_is_valid("#¤%&?_only_numbers_letters_and_underscore_allowed")); | |
279 | } | |
280 | ||
b8c83cfc ZJS |
281 | static void test_env_value_is_valid(void) { |
282 | assert_se(env_value_is_valid("")); | |
283 | assert_se(env_value_is_valid("głąb kapuściany")); | |
284 | assert_se(env_value_is_valid("printf \"\\x1b]0;<mock-chroot>\\x07<mock-chroot>\"")); | |
b4346b9a FB |
285 | assert_se(env_value_is_valid("tab\tcharacter")); |
286 | assert_se(env_value_is_valid("new\nline")); | |
b8c83cfc ZJS |
287 | } |
288 | ||
289 | static void test_env_assignment_is_valid(void) { | |
290 | assert_se(env_assignment_is_valid("a=")); | |
291 | assert_se(env_assignment_is_valid("b=głąb kapuściany")); | |
292 | assert_se(env_assignment_is_valid("c=\\007\\009\\011")); | |
293 | assert_se(env_assignment_is_valid("e=printf \"\\x1b]0;<mock-chroot>\\x07<mock-chroot>\"")); | |
b4346b9a FB |
294 | assert_se(env_assignment_is_valid("f=tab\tcharacter")); |
295 | assert_se(env_assignment_is_valid("g=new\nline")); | |
b8c83cfc ZJS |
296 | |
297 | assert_se(!env_assignment_is_valid("=")); | |
298 | assert_se(!env_assignment_is_valid("a b=")); | |
299 | assert_se(!env_assignment_is_valid("a =")); | |
300 | assert_se(!env_assignment_is_valid(" b=")); | |
301 | /* no dots or dashes: http://tldp.org/LDP/abs/html/gotchas.html */ | |
302 | assert_se(!env_assignment_is_valid("a.b=")); | |
303 | assert_se(!env_assignment_is_valid("a-b=")); | |
304 | assert_se(!env_assignment_is_valid("\007=głąb kapuściany")); | |
305 | assert_se(!env_assignment_is_valid("c\009=\007\009\011")); | |
306 | assert_se(!env_assignment_is_valid("głąb=printf \"\x1b]0;<mock-chroot>\x07<mock-chroot>\"")); | |
307 | } | |
308 | ||
16eefcaf RC |
309 | static void test_deserialize_environment(void) { |
310 | _cleanup_strv_free_ char **env = strv_new("A=1", NULL); | |
311 | ||
16eefcaf | 312 | assert_se(deserialize_environment(&env, "env=B=2") >= 0); |
ea43bdd1 | 313 | assert_se(deserialize_environment(&env, "env=FOO%%=a\\177b\\nc\\td e") >= 0); |
16eefcaf | 314 | |
ea43bdd1 | 315 | assert_se(strv_equal(env, STRV_MAKE("A=1", "B=2", "FOO%%=a\177b\nc\td e"))); |
c7d797bb LR |
316 | |
317 | assert_se(deserialize_environment(&env, "env=foo\\") < 0); | |
318 | assert_se(deserialize_environment(&env, "env=bar\\_baz") < 0); | |
16eefcaf RC |
319 | } |
320 | ||
a096d8c8 ZJS |
321 | static void test_serialize_environment(void) { |
322 | char fn[] = "/tmp/test-env-util.XXXXXXX"; | |
323 | int fd, r; | |
324 | _cleanup_fclose_ FILE *f = NULL; | |
325 | ||
326 | _cleanup_strv_free_ char **env = strv_new("A=1", | |
327 | "B=2", | |
328 | "C=ąęółń", | |
329 | "D=D=a\\x0Ab", | |
ea43bdd1 | 330 | "FOO%%=a\177b\nc\td e", |
a096d8c8 ZJS |
331 | NULL); |
332 | _cleanup_strv_free_ char **env2 = NULL; | |
333 | ||
334 | fd = mkostemp_safe(fn); | |
335 | assert_se(fd >= 0); | |
336 | ||
337 | assert_se(f = fdopen(fd, "r+")); | |
338 | ||
339 | assert_se(serialize_environment(f, env) == 0); | |
340 | assert_se(fflush_and_check(f) == 0); | |
341 | ||
342 | rewind(f); | |
343 | ||
344 | for (;;) { | |
345 | char line[LINE_MAX]; | |
346 | const char *l; | |
347 | ||
348 | if (!fgets(line, sizeof line, f)) | |
349 | break; | |
350 | ||
351 | char_array_0(line); | |
352 | l = strstrip(line); | |
353 | ||
354 | r = deserialize_environment(&env2, l); | |
355 | assert_se(r == 1); | |
356 | } | |
357 | assert_se(feof(f)); | |
358 | ||
359 | assert_se(strv_equal(env, env2)); | |
360 | ||
361 | unlink(fn); | |
362 | } | |
363 | ||
1f28b2de | 364 | int main(int argc, char *argv[]) { |
940bd473 | 365 | test_strv_env_delete(); |
6162512c | 366 | test_strv_env_get(); |
940bd473 TA |
367 | test_strv_env_unset(); |
368 | test_strv_env_set(); | |
1f28b2de | 369 | test_strv_env_merge(); |
37f3ffca | 370 | test_env_strv_get_n(); |
ccad1fd0 ZJS |
371 | test_replace_env(false); |
372 | test_replace_env(true); | |
f50ce8fc ZJS |
373 | test_replace_env2(false); |
374 | test_replace_env2(true); | |
ccad1fd0 | 375 | test_replace_env_argv(); |
4d1a6904 | 376 | test_env_clean(); |
8354c34e | 377 | test_env_name_is_valid(); |
b8c83cfc ZJS |
378 | test_env_value_is_valid(); |
379 | test_env_assignment_is_valid(); | |
16eefcaf | 380 | test_deserialize_environment(); |
a096d8c8 | 381 | test_serialize_environment(); |
a6ff950e | 382 | |
5f7c426e | 383 | return 0; |
c24eb49e | 384 | } |