xref: /netbsd-src/tests/lib/libc/stdlib/t_getenv.c (revision 046047ef8e133e8f53772996ab8e3cd26b6929fc)
1 /*	$NetBSD: t_getenv.c,v 1.1 2011/07/07 15:50:23 jruoho Exp $ */
2 
3 /*-
4  * Copyright (c) 2010 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Christos Zoulas
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in
18  *    the documentation and/or other materials provided with the
19  *    distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 /*-
36  * Copyright (c) 2010 The NetBSD Foundation, Inc.
37  * All rights reserved.
38  *
39  * This code is derived from software contributed to The NetBSD Foundation
40  * by Matthias Scheler.
41  *
42  * Redistribution and use in source and binary forms, with or without
43  * modification, are permitted provided that the following conditions
44  * are met:
45  * 1. Redistributions of source code must retain the above copyright
46  *    notice, this list of conditions and the following disclaimer.
47  * 2. Redistributions in binary form must reproduce the above copyright
48  *    notice, this list of conditions and the following disclaimer in the
49  *    documentation and/or other materials provided with the distribution.
50  *
51  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
52  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
53  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
54  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
55  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
56  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
57  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
58  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
59  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
60  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
61  * POSSIBILITY OF SUCH DAMAGE.
62  */
63 #include <sys/cdefs.h>
64 __RCSID("$NetBSD: t_getenv.c,v 1.1 2011/07/07 15:50:23 jruoho Exp $");
65 
66 #include <atf-c.h>
67 #include <errno.h>
68 #include <pthread.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #include <time.h>
73 
74 #define	THREADED_NUM_THREADS	8
75 #define	THREADED_NUM_VARS	16
76 #define	THREADED_VAR_NAME	"THREADED%zu"
77 #define	THREADED_RUN_TIME	10
78 
79 extern char	**environ;
80 static void	 *thread_getenv_r(void *);
81 static void	 *thread_putenv(void *);
82 static void	 *thread_setenv(void *);
83 static void	 *thread_unsetenv(void *);
84 
85 static void *
86 thread_getenv_r(void *arg)
87 {
88 	time_t endtime;
89 
90 	endtime = *(time_t *)arg;
91 	do {
92 		size_t i;
93 		char name[32], value[128];
94 
95 		i = lrand48() % THREADED_NUM_VARS;
96 		(void)snprintf(name, sizeof(name), THREADED_VAR_NAME, i);
97 
98 		if (getenv_r(name, value, sizeof(value)) == -1) {
99 			ATF_CHECK(errno == ENOENT);
100 		}
101 	} while (time(NULL) < endtime);
102 
103 	return NULL;
104 }
105 
106 
107 static void *
108 thread_putenv(void *arg)
109 {
110 	time_t endtime;
111 	size_t i;
112 	static char vars[THREADED_NUM_VARS][128];
113 
114 	for (i = 0; i < THREADED_NUM_VARS; i++) {
115 		(void)snprintf(vars[i], sizeof(vars[i]),
116 		    THREADED_VAR_NAME "=putenv %ld", i, lrand48());
117 	}
118 
119 	endtime = *(time_t *)arg;
120 	do {
121 		char name[128];
122 
123 		i = lrand48() % THREADED_NUM_VARS;
124 		(void)strlcpy(name, vars[i], sizeof(name));
125 		*strchr(name, '=') = '\0';
126 
127 		ATF_CHECK(unsetenv(name) != -1);
128 		ATF_CHECK(putenv(vars[i]) != -1);
129 	} while (time(NULL) < endtime);
130 
131 	return NULL;
132 }
133 
134 static void *
135 thread_setenv(void *arg)
136 {
137 	time_t endtime;
138 
139 	endtime = *(time_t *)arg;
140 	do {
141 		size_t i;
142 		char name[32], value[64];
143 
144 		i = lrand48() % THREADED_NUM_VARS;
145 		(void)snprintf(name, sizeof(name), THREADED_VAR_NAME, i);
146 		(void)snprintf(value, sizeof(value), "setenv %ld", lrand48());
147 
148 		ATF_CHECK(setenv(name, value, 1) != -1);
149 	} while (time(NULL) < endtime);
150 
151 	return NULL;
152 }
153 
154 static void *
155 thread_unsetenv(void *arg)
156 {
157 	time_t endtime;
158 
159 	endtime = *(time_t *)arg;
160 	do {
161 		size_t i;
162 		char name[32];
163 
164 		i = lrand48() % THREADED_NUM_VARS;
165 		(void)snprintf(name, sizeof(name), THREADED_VAR_NAME, i);
166 
167 		ATF_CHECK(unsetenv(name) != -1);
168 	} while (time(NULL) < endtime);
169 
170 	return NULL;
171 }
172 
173 ATF_TC(clearenv_basic);
174 ATF_TC_HEAD(clearenv_basic, tc)
175 {
176 	atf_tc_set_md_var(tc, "descr",
177 	    "Test user clearing environment directly");
178 }
179 
180 ATF_TC_BODY(clearenv_basic, tc)
181 {
182 	char name[1024], value[1024];
183 
184 	for (size_t i = 0; i < 1024; i++) {
185 		snprintf(name, sizeof(name), "crap%zu", i);
186 		snprintf(value, sizeof(value), "%zu", i);
187 		ATF_CHECK(setenv(name, value, 1) != -1);
188 	}
189 
190 	*environ = NULL;
191 
192 	for (size_t i = 0; i < 1; i++) {
193 		snprintf(name, sizeof(name), "crap%zu", i);
194 		snprintf(value, sizeof(value), "%zu", i);
195 		ATF_CHECK(setenv(name, value, 1) != -1);
196 	}
197 
198 	ATF_CHECK_STREQ(getenv("crap0"), "0");
199 	ATF_CHECK(getenv("crap1") == NULL);
200 	ATF_CHECK(getenv("crap2") == NULL);
201 }
202 
203 ATF_TC(getenv_basic);
204 ATF_TC_HEAD(getenv_basic, tc)
205 {
206 	atf_tc_set_md_var(tc, "descr",
207 	    "Test setenv(3), getenv(3)");
208 }
209 
210 ATF_TC_BODY(getenv_basic, tc)
211 {
212 	ATF_CHECK(setenv("EVIL", "very=bad", 1) != -1);
213 	ATF_CHECK_STREQ(getenv("EVIL"), "very=bad");
214 	ATF_CHECK(getenv("EVIL=very") == NULL);
215 	ATF_CHECK(unsetenv("EVIL") != -1);
216 }
217 
218 ATF_TC(getenv_r_thread);
219 ATF_TC_HEAD(getenv_r_thread, tc)
220 {
221 	char timeout[32];
222 
223 	atf_tc_set_md_var(tc, "descr", "Test getenv_r(3) with threads");
224 
225 	(void)snprintf(timeout, sizeof(timeout), "%d", THREADED_RUN_TIME + 5);
226 
227 	atf_tc_set_md_var(tc, "timeout", timeout);
228 }
229 
230 ATF_TC_BODY(getenv_r_thread, tc)
231 {
232 	pthread_t threads[THREADED_NUM_THREADS];
233 	time_t endtime;
234 	size_t i, j;
235 
236 	endtime = time(NULL) + THREADED_RUN_TIME;
237 
238 	for (i = j = 0; j < 2; j++) {
239 
240 		ATF_CHECK(pthread_create(&threads[i++], NULL, thread_getenv_r,
241 		    &endtime) == 0);
242 	}
243 
244 	for (j = 0; j < i; j++)
245 		ATF_CHECK(pthread_join(threads[j], NULL) == 0);
246 }
247 
248 ATF_TC(putenv_basic);
249 ATF_TC_HEAD(putenv_basic, tc)
250 {
251 	atf_tc_set_md_var(tc, "descr",
252 	    "Test putenv(3), getenv(3), unsetenv(3)");
253 }
254 
255 
256 ATF_TC_BODY(putenv_basic, tc)
257 {
258 	char string[1024];
259 
260 	snprintf(string, sizeof(string), "crap=true");
261 	ATF_CHECK(putenv(string) != -1);
262 	ATF_CHECK_STREQ(getenv("crap"), "true");
263 	string[1] = 'l';
264 	ATF_CHECK_STREQ(getenv("clap"), "true");
265 	ATF_CHECK(getenv("crap") == NULL);
266 	string[1] = 'r';
267 	ATF_CHECK(unsetenv("crap") != -1);
268 	ATF_CHECK(getenv("crap") == NULL);
269 
270 	ATF_CHECK_ERRNO(EINVAL, putenv(NULL) == -1);
271 	ATF_CHECK_ERRNO(EINVAL, putenv(__UNCONST("val")) == -1);
272 	ATF_CHECK_ERRNO(EINVAL, putenv(__UNCONST("=val")) == -1);
273 }
274 
275 ATF_TC(putenv_thread);
276 ATF_TC_HEAD(putenv_thread, tc)
277 {
278 	char timeout[32];
279 
280 	atf_tc_set_md_var(tc, "descr", "Test concurrent access by putenv(3)");
281 
282 	(void)snprintf(timeout, sizeof(timeout), "%d", THREADED_RUN_TIME + 5);
283 
284 	atf_tc_set_md_var(tc, "timeout", timeout);
285 }
286 
287 ATF_TC_BODY(putenv_thread, tc)
288 {
289 	pthread_t threads[THREADED_NUM_THREADS];
290 	time_t endtime;
291 	size_t i, j;
292 
293 	endtime = time(NULL) + THREADED_RUN_TIME;
294 
295 	for (i = j = 0; j < 2; j++) {
296 
297 		ATF_CHECK(pthread_create(&threads[i++], NULL, thread_putenv,
298 		    &endtime) == 0);
299 	}
300 
301 	for (j = 0; j < i; j++)
302 		ATF_CHECK(pthread_join(threads[j], NULL) == 0);
303 }
304 
305 ATF_TC(setenv_basic);
306 ATF_TC_HEAD(setenv_basic, tc)
307 {
308 	atf_tc_set_md_var(tc, "descr",
309 	    "Test setenv(3), getenv(3), unsetenv(3)");
310 	atf_tc_set_md_var(tc, "timeout", "300");
311 }
312 
313 ATF_TC_BODY(setenv_basic, tc)
314 {
315 	const size_t numvars = 8192;
316 	size_t i, offset;
317 	char name[1024];
318 	char value[1024];
319 
320 	offset = lrand48();
321 	for (i = 0; i < numvars; i++) {
322 		(void)snprintf(name, sizeof(name), "var%zu",
323 		    (i * 7 + offset) % numvars);
324 		(void)snprintf(value, sizeof(value), "value%ld", lrand48());
325 		ATF_CHECK(setenv(name, value, 1) != -1);
326 		ATF_CHECK(setenv(name, "foo", 0) != -1);
327 		ATF_CHECK_STREQ(getenv(name), value);
328 	}
329 
330 	offset = lrand48();
331 	for (i = 0; i < numvars; i++) {
332 		(void)snprintf(name, sizeof(name), "var%zu",
333 		    (i * 11 + offset) % numvars);
334 		ATF_CHECK(unsetenv(name) != -1);
335 		ATF_CHECK(getenv(name) == NULL);
336 		ATF_CHECK(unsetenv(name) != -1);
337 	}
338 
339 	ATF_CHECK_ERRNO(EINVAL, setenv(NULL, "val", 1) == -1);
340 	ATF_CHECK_ERRNO(EINVAL, setenv("", "val", 1) == -1);
341 	ATF_CHECK_ERRNO(EINVAL, setenv("v=r", "val", 1) == -1);
342 	ATF_CHECK_ERRNO(EINVAL, setenv("var", NULL, 1) == -1);
343 
344 	ATF_CHECK(setenv("var", "=val", 1) == 0);
345 	ATF_CHECK_STREQ(getenv("var"), "=val");
346 }
347 
348 ATF_TC(setenv_mixed);
349 ATF_TC_HEAD(setenv_mixed, tc)
350 {
351 	atf_tc_set_md_var(tc, "descr",
352 	    "Test mixing setenv(3), unsetenv(3) and putenv(3)");
353 }
354 
355 
356 ATF_TC_BODY(setenv_mixed, tc)
357 {
358 	char string[32];
359 
360 	(void)strcpy(string, "mixedcrap=putenv");
361 
362 	ATF_CHECK(setenv("mixedcrap", "setenv", 1) != -1);
363 	ATF_CHECK_STREQ(getenv("mixedcrap"), "setenv");
364 	ATF_CHECK(putenv(string) != -1);
365 	ATF_CHECK_STREQ(getenv("mixedcrap"), "putenv");
366 	ATF_CHECK(unsetenv("mixedcrap") != -1);
367 	ATF_CHECK(getenv("mixedcrap") == NULL);
368 
369 	ATF_CHECK(putenv(string) != -1);
370 	ATF_CHECK_STREQ(getenv("mixedcrap"), "putenv");
371 	ATF_CHECK(setenv("mixedcrap", "setenv", 1) != -1);
372 	ATF_CHECK_STREQ(getenv("mixedcrap"), "setenv");
373 	ATF_CHECK(unsetenv("mixedcrap") != -1);
374 	ATF_CHECK(getenv("mixedcrap") == NULL);
375 }
376 
377 ATF_TC(setenv_thread);
378 ATF_TC_HEAD(setenv_thread, tc)
379 {
380 	char timeout[32];
381 
382 	atf_tc_set_md_var(tc, "descr", "Test concurrent access by setenv(3)");
383 
384 	(void)snprintf(timeout, sizeof(timeout), "%d", THREADED_RUN_TIME + 5);
385 
386 	atf_tc_set_md_var(tc, "timeout", timeout);
387 }
388 
389 ATF_TC_BODY(setenv_thread, tc)
390 {
391 	pthread_t threads[THREADED_NUM_THREADS];
392 	time_t endtime;
393 	size_t i, j;
394 
395 	endtime = time(NULL) + THREADED_RUN_TIME;
396 
397 	for (i = j = 0; j < 2; j++) {
398 
399 		ATF_CHECK(pthread_create(&threads[i++], NULL, thread_setenv,
400 		    &endtime) == 0);
401 	}
402 
403 	for (j = 0; j < i; j++)
404 		ATF_CHECK(pthread_join(threads[j], NULL) == 0);
405 }
406 
407 ATF_TC(unsetenv_thread);
408 ATF_TC_HEAD(unsetenv_thread, tc)
409 {
410 	char timeout[32];
411 
412 	atf_tc_set_md_var(tc, "descr", "Test unsetenv(3) with threads");
413 
414 	(void)snprintf(timeout, sizeof(timeout), "%d", THREADED_RUN_TIME + 5);
415 
416 	atf_tc_set_md_var(tc, "timeout", timeout);
417 }
418 
419 ATF_TC_BODY(unsetenv_thread, tc)
420 {
421 	pthread_t threads[THREADED_NUM_THREADS];
422 	time_t endtime;
423 	size_t i, j;
424 
425 	endtime = time(NULL) + THREADED_RUN_TIME;
426 
427 	for (i = j = 0; j < 2; j++) {
428 
429 		ATF_CHECK(pthread_create(&threads[i++], NULL, thread_unsetenv,
430 		    &endtime) == 0);
431 	}
432 
433 	for (j = 0; j < i; j++)
434 		ATF_CHECK(pthread_join(threads[j], NULL) == 0);
435 }
436 
437 ATF_TP_ADD_TCS(tp)
438 {
439 	ATF_TP_ADD_TC(tp, clearenv_basic);
440 	ATF_TP_ADD_TC(tp, getenv_basic);
441 	ATF_TP_ADD_TC(tp, getenv_r_thread);
442 	ATF_TP_ADD_TC(tp, putenv_basic);
443 	ATF_TP_ADD_TC(tp, putenv_thread);
444 	ATF_TP_ADD_TC(tp, setenv_basic);
445 	ATF_TP_ADD_TC(tp, setenv_mixed);
446 	ATF_TP_ADD_TC(tp, setenv_thread);
447 	ATF_TP_ADD_TC(tp, unsetenv_thread);
448 
449 	return atf_no_error();
450 }
451