xref: /freebsd/lib/libc/stdlib/getenv.c (revision 1669d8afc64812c8d2d1d147ae1fd42ff441e1b1)
1 /*-
2  * Copyright (c) 2007 Sean C. Farley <scf@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification, immediately at the beginning of the file.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 #include <sys/types.h>
27 #include <err.h>
28 #include <errno.h>
29 #include <stdbool.h>
30 #include <stddef.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37 
38 
39 static const char CorruptEnvFindMsg[] =
40     "environment corrupt; unable to find %.*s";
41 static const char CorruptEnvValueMsg[] =
42     "environment corrupt; missing value for %s";
43 
44 
45 /*
46  * Standard environ.  environ variable is exposed to entire process.
47  *
48  *	origEnviron:	Upon cleanup on unloading of library or failure, this
49  *			allows environ to return to as it was before.
50  *	environSize:	Number of variables environ can hold.  Can only
51  *			increase.
52  *	intEnviron:	Internally-built environ.  Exposed via environ during
53  *			(re)builds of the environment.
54  */
55 extern char **environ;
56 static char **origEnviron;
57 static char **intEnviron = NULL;
58 static int environSize = 0;
59 
60 /*
61  * Array of environment variables built from environ.  Each element records:
62  *	name:		Pointer to name=value string
63  *	name length:	Length of name not counting '=' character
64  *	value:		Pointer to value within same string as name
65  *	value size:	Size (not length) of space for value not counting the
66  *			nul character
67  *	active state:	true/false value to signify whether variable is active.
68  *			Useful since multiple variables with the same name can
69  *			co-exist.  At most, one variable can be active at any
70  *			one time.
71  *	putenv:		Created from putenv() call.  This memory must not be
72  *			reused.
73  */
74 static struct envVars {
75 	size_t nameLen;
76 	size_t valueSize;
77 	char *name;
78 	char *value;
79 	bool active;
80 	bool putenv;
81 } *envVars = NULL;
82 
83 /*
84  * Environment array information.
85  *
86  *	envActive:	Number of active variables in array.
87  *	envVarsSize:	Size of array.
88  *	envVarsTotal:	Number of total variables in array (active or not).
89  */
90 static int envActive = 0;
91 static int envVarsSize = 0;
92 static int envVarsTotal = 0;
93 
94 
95 /* Deinitialization of new environment. */
96 static void __attribute__ ((destructor)) __clean_env_destructor(void);
97 
98 
99 /*
100  * Inline strlen() for performance.  Also, perform check for an equals sign.
101  * Cheaper here than peforming a strchr() later.
102  */
103 static inline size_t
104 __strleneq(const char *str)
105 {
106 	const char *s;
107 
108 	for (s = str; *s != '\0'; ++s)
109 		if (*s == '=')
110 			return (0);
111 
112 	return (s - str);
113 }
114 
115 
116 /*
117  * Comparison of an environment name=value to a name.
118  */
119 static inline bool
120 strncmpeq(const char *nameValue, const char *name, size_t nameLen)
121 {
122 	if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=')
123 		return (true);
124 
125 	return (false);
126 }
127 
128 
129 /*
130  * Using environment, returns pointer to value associated with name, if any,
131  * else NULL.  If the onlyActive flag is set to true, only variables that are
132  * active are returned else all are.
133  */
134 static inline char *
135 __findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive)
136 {
137 	int ndx;
138 
139 	/*
140 	 * Find environment variable from end of array (more likely to be
141 	 * active).  A variable created by putenv is always active or it is not
142 	 * tracked in the array.
143 	 */
144 	for (ndx = *envNdx; ndx >= 0; ndx--)
145 		if (envVars[ndx].putenv) {
146 			if (strncmpeq(envVars[ndx].name, name, nameLen)) {
147 				*envNdx = ndx;
148 				return (envVars[ndx].name + nameLen +
149 				    sizeof ("=") - 1);
150 			}
151 		} else if ((!onlyActive || envVars[ndx].active) &&
152 		    (envVars[ndx].nameLen == nameLen &&
153 		    strncmpeq(envVars[ndx].name, name, nameLen))) {
154 			*envNdx = ndx;
155 			return (envVars[ndx].value);
156 		}
157 
158 	return (NULL);
159 }
160 
161 
162 /*
163  * Using environ, returns pointer to value associated with name, if any, else
164  * NULL.  Used on the original environ passed into the program.
165  */
166 static char *
167 __findenv_environ(const char *name, size_t nameLen)
168 {
169 	int envNdx;
170 
171 	/* Check for non-existant environment. */
172 	if (environ == NULL)
173 		return (NULL);
174 
175 	/* Find variable within environ. */
176 	for (envNdx = 0; environ[envNdx] != NULL; envNdx++)
177 		if (strncmpeq(environ[envNdx], name, nameLen))
178 			return (&(environ[envNdx][nameLen + sizeof("=") - 1]));
179 
180 	return (NULL);
181 }
182 
183 
184 /*
185  * Remove variable added by putenv() from variable tracking array.
186  */
187 static void
188 __remove_putenv(int envNdx)
189 {
190 	envVarsTotal--;
191 	if (envVarsTotal > envNdx)
192 		memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]),
193 		    (envVarsTotal - envNdx) * sizeof (*envVars));
194 	memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars));
195 
196 	return;
197 }
198 
199 
200 /*
201  * Deallocate the environment built from environ as well as environ then set
202  * both to NULL.  Eases debugging of memory leaks.
203  */
204 static void
205 __clean_env(bool freeVars)
206 {
207 	int envNdx;
208 
209 	/* Deallocate environment and environ if created by *env(). */
210 	if (envVars != NULL) {
211 		for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--)
212 			/* Free variables or deactivate them. */
213 			if (envVars[envNdx].putenv) {
214 				if (!freeVars)
215 					__remove_putenv(envNdx);
216 			} else {
217 				if (freeVars)
218 					free(envVars[envNdx].name);
219 				else
220 					envVars[envNdx].active = false;
221 			}
222 		if (freeVars) {
223 			free(envVars);
224 			envVars = NULL;
225 		} else
226 			envActive = 0;
227 
228 		/* Restore original environ if it has not updated by program. */
229 		if (origEnviron != NULL) {
230 			if (environ == intEnviron)
231 				environ = origEnviron;
232 			free(intEnviron);
233 			intEnviron = NULL;
234 			environSize = 0;
235 		}
236 	}
237 
238 	return;
239 }
240 
241 
242 /*
243  * Using the environment, rebuild the environ array for use by other C library
244  * calls that depend upon it.
245  */
246 static int
247 __rebuild_environ(int newEnvironSize)
248 {
249 	char **tmpEnviron;
250 	int envNdx;
251 	int environNdx;
252 	int tmpEnvironSize;
253 
254 	/* Resize environ. */
255 	if (newEnvironSize > environSize) {
256 		tmpEnvironSize = newEnvironSize * 2;
257 		tmpEnviron = realloc(intEnviron, sizeof (*intEnviron) *
258 		    (tmpEnvironSize + 1));
259 		if (tmpEnviron == NULL)
260 			return (-1);
261 		environSize = tmpEnvironSize;
262 		intEnviron = tmpEnviron;
263 	}
264 	envActive = newEnvironSize;
265 
266 	/* Assign active variables to environ. */
267 	for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--)
268 		if (envVars[envNdx].active)
269 			intEnviron[environNdx++] = envVars[envNdx].name;
270 	intEnviron[environNdx] = NULL;
271 
272 	/* Always set environ which may have been replaced by program. */
273 	environ = intEnviron;
274 
275 	return (0);
276 }
277 
278 
279 /*
280  * Enlarge new environment.
281  */
282 static inline bool
283 __enlarge_env(void)
284 {
285 	int newEnvVarsSize;
286 	struct envVars *tmpEnvVars;
287 
288 	envVarsTotal++;
289 	if (envVarsTotal > envVarsSize) {
290 		newEnvVarsSize = envVarsTotal * 2;
291 		tmpEnvVars = realloc(envVars, sizeof (*envVars) *
292 		    newEnvVarsSize);
293 		if (tmpEnvVars == NULL) {
294 			envVarsTotal--;
295 			return (false);
296 		}
297 		envVarsSize = newEnvVarsSize;
298 		envVars = tmpEnvVars;
299 	}
300 
301 	return (true);
302 }
303 
304 
305 /*
306  * Using environ, build an environment for use by standard C library calls.
307  */
308 static int
309 __build_env(void)
310 {
311 	char **env;
312 	int activeNdx;
313 	int envNdx;
314 	int savedErrno;
315 	size_t nameLen;
316 
317 	/* Check for non-existant environment. */
318 	if (environ == NULL || environ[0] == NULL)
319 		return (0);
320 
321 	/* Count environment variables. */
322 	for (env = environ, envVarsTotal = 0; *env != NULL; env++)
323 		envVarsTotal++;
324 	envVarsSize = envVarsTotal * 2;
325 
326 	/* Create new environment. */
327 	envVars = calloc(1, sizeof (*envVars) * envVarsSize);
328 	if (envVars == NULL)
329 		goto Failure;
330 
331 	/* Copy environ values and keep track of them. */
332 	for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) {
333 		envVars[envNdx].putenv = false;
334 		envVars[envNdx].name =
335 		    strdup(environ[envVarsTotal - envNdx - 1]);
336 		if (envVars[envNdx].name == NULL)
337 			goto Failure;
338 		envVars[envNdx].value = strchr(envVars[envNdx].name, '=');
339 		if (envVars[envNdx].value != NULL) {
340 			envVars[envNdx].value++;
341 			envVars[envNdx].valueSize =
342 			    strlen(envVars[envNdx].value);
343 		} else {
344 			warnx(CorruptEnvValueMsg, envVars[envNdx].name);
345 			errno = EFAULT;
346 			goto Failure;
347 		}
348 
349 		/*
350 		 * Find most current version of variable to make active.  This
351 		 * will prevent multiple active variables from being created
352 		 * during this initialization phase.
353 		 */
354 		nameLen = envVars[envNdx].value - envVars[envNdx].name - 1;
355 		envVars[envNdx].nameLen = nameLen;
356 		activeNdx = envVarsTotal - 1;
357 		if (__findenv(envVars[envNdx].name, nameLen, &activeNdx,
358 		    false) == NULL) {
359 			warnx(CorruptEnvFindMsg, (int)nameLen,
360 			    envVars[envNdx].name);
361 			errno = EFAULT;
362 			goto Failure;
363 		}
364 		envVars[activeNdx].active = true;
365 	}
366 
367 	/* Create a new environ. */
368 	origEnviron = environ;
369 	environ = NULL;
370 	if (__rebuild_environ(envVarsTotal) == 0)
371 		return (0);
372 
373 Failure:
374 	savedErrno = errno;
375 	__clean_env(true);
376 	errno = savedErrno;
377 
378 	return (-1);
379 }
380 
381 
382 /*
383  * Destructor function with default argument to __clean_env().
384  */
385 static void
386 __clean_env_destructor(void)
387 {
388 	__clean_env(true);
389 
390 	return;
391 }
392 
393 
394 /*
395  * Returns the value of a variable or NULL if none are found.
396  */
397 char *
398 getenv(const char *name)
399 {
400 	int envNdx;
401 	size_t nameLen;
402 
403 	/* Check for malformed name. */
404 	if (name == NULL || (nameLen = __strleneq(name)) == 0) {
405 		errno = EINVAL;
406 		return (NULL);
407 	}
408 
409 	/*
410 	 * Find environment variable via environ if no changes have been made
411 	 * via a *env() call or environ has been replaced by a running program,
412 	 * otherwise, use the rebuilt environment.
413 	 */
414 	if (envVars == NULL || environ != intEnviron)
415 		return (__findenv_environ(name, nameLen));
416 	else {
417 		envNdx = envVarsTotal - 1;
418 		return (__findenv(name, nameLen, &envNdx, true));
419 	}
420 }
421 
422 
423 /*
424  * Set the value of a variable.  Older settings are labeled as inactive.  If an
425  * older setting has enough room to store the new value, it will be reused.  No
426  * previous variables are ever freed here to avoid causing a segmentation fault
427  * in a user's code.
428  *
429  * The variables nameLen and valueLen are passed into here to allow the caller
430  * to calculate the length by means besides just strlen().
431  */
432 static int
433 __setenv(const char *name, size_t nameLen, const char *value, int overwrite)
434 {
435 	bool reuse;
436 	char *env;
437 	int envNdx;
438 	int newEnvActive;
439 	size_t valueLen;
440 
441 	/* Find existing environment variable large enough to use. */
442 	envNdx = envVarsTotal - 1;
443 	newEnvActive = envActive;
444 	valueLen = strlen(value);
445 	reuse = false;
446 	if (__findenv(name, nameLen, &envNdx, false) != NULL) {
447 		/* Deactivate entry if overwrite is allowed. */
448 		if (envVars[envNdx].active) {
449 			if (overwrite == 0)
450 				return (0);
451 			envVars[envNdx].active = false;
452 			newEnvActive--;
453 		}
454 
455 		/* putenv() created variable cannot be reused. */
456 		if (envVars[envNdx].putenv)
457 			__remove_putenv(envNdx);
458 
459 		/* Entry is large enough to reuse. */
460 		else if (envVars[envNdx].valueSize >= valueLen)
461 			reuse = true;
462 	}
463 
464 	/* Create new variable if none was found of sufficient size. */
465 	if (! reuse) {
466 		/* Enlarge environment. */
467 		envNdx = envVarsTotal;
468 		if (!__enlarge_env())
469 			return (-1);
470 
471 		/* Create environment entry. */
472 		envVars[envNdx].name = malloc(nameLen + sizeof ("=") +
473 		    valueLen);
474 		if (envVars[envNdx].name == NULL) {
475 			envVarsTotal--;
476 			return (-1);
477 		}
478 		envVars[envNdx].nameLen = nameLen;
479 		envVars[envNdx].valueSize = valueLen;
480 
481 		/* Save name of name/value pair. */
482 		env = stpcpy(envVars[envNdx].name, name);
483 		if ((envVars[envNdx].name)[nameLen] != '=')
484 			env = stpcpy(env, "=");
485 	}
486 	else
487 		env = envVars[envNdx].value;
488 
489 	/* Save value of name/value pair. */
490 	strcpy(env, value);
491 	envVars[envNdx].value = env;
492 	envVars[envNdx].active = true;
493 	newEnvActive++;
494 
495 	/* No need to rebuild environ if an active variable was reused. */
496 	if (reuse && newEnvActive == envActive)
497 		return (0);
498 	else
499 		return (__rebuild_environ(newEnvActive));
500 }
501 
502 
503 /*
504  * If the program attempts to replace the array of environment variables
505  * (environ) environ, then deactivate all variables and merge in the new list
506  * from environ.
507  */
508 static int
509 __merge_environ(void)
510 {
511 	char **env;
512 	char *equals;
513 
514 	/* environ has been replaced.  clean up everything. */
515 	if (envVarsTotal > 0 && environ != intEnviron) {
516 		/* Deactivate all environment variables. */
517 		if (envActive > 0) {
518 			origEnviron = NULL;
519 			__clean_env(false);
520 		}
521 
522 		/*
523 		 * Insert new environ into existing, yet deactivated,
524 		 * environment array.
525 		 */
526 		origEnviron = environ;
527 		if (origEnviron != NULL)
528 			for (env = origEnviron; *env != NULL; env++) {
529 				if ((equals = strchr(*env, '=')) == NULL) {
530 					warnx(CorruptEnvValueMsg, *env);
531 					errno = EFAULT;
532 					return (-1);
533 				}
534 				if (__setenv(*env, equals - *env, equals + 1,
535 				    1) == -1)
536 					return (-1);
537 			}
538 	}
539 
540 	return (0);
541 }
542 
543 
544 /*
545  * The exposed setenv() that peforms a few tests before calling the function
546  * (__setenv()) that does the actual work of inserting a variable into the
547  * environment.
548  */
549 int
550 setenv(const char *name, const char *value, int overwrite)
551 {
552 	size_t nameLen;
553 
554 	/* Check for malformed name. */
555 	if (name == NULL || (nameLen = __strleneq(name)) == 0) {
556 		errno = EINVAL;
557 		return (-1);
558 	}
559 
560 	/* Initialize environment. */
561 	if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
562 		return (-1);
563 
564 	return (__setenv(name, nameLen, value, overwrite));
565 }
566 
567 
568 /*
569  * Insert a "name=value" string into the environment.  Special settings must be
570  * made to keep setenv() from reusing this memory block and unsetenv() from
571  * allowing it to be tracked.
572  */
573 int
574 putenv(char *string)
575 {
576 	char *equals;
577 	int envNdx;
578 	int newEnvActive;
579 	size_t nameLen;
580 
581 	/* Check for malformed argument. */
582 	if (string == NULL || (equals = strchr(string, '=')) == NULL ||
583 	    (nameLen = equals - string) == 0) {
584 		errno = EINVAL;
585 		return (-1);
586 	}
587 
588 	/* Initialize environment. */
589 	if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
590 		return (-1);
591 
592 	/* Deactivate previous environment variable. */
593 	envNdx = envVarsTotal - 1;
594 	newEnvActive = envActive;
595 	if (__findenv(string, nameLen, &envNdx, true) != NULL) {
596 		/* Reuse previous putenv slot. */
597 		if (envVars[envNdx].putenv) {
598 			envVars[envNdx].name = string;
599 			return (__rebuild_environ(envActive));
600 		} else {
601 			newEnvActive--;
602 			envVars[envNdx].active = false;
603 		}
604 	}
605 
606 	/* Enlarge environment. */
607 	envNdx = envVarsTotal;
608 	if (!__enlarge_env())
609 		return (-1);
610 
611 	/* Create environment entry. */
612 	envVars[envNdx].name = string;
613 	envVars[envNdx].nameLen = -1;
614 	envVars[envNdx].value = NULL;
615 	envVars[envNdx].valueSize = -1;
616 	envVars[envNdx].putenv = true;
617 	envVars[envNdx].active = true;
618 	newEnvActive++;
619 
620 	return (__rebuild_environ(newEnvActive));
621 }
622 
623 
624 /*
625  * Unset variable with the same name by flagging it as inactive.  No variable is
626  * ever freed.
627  */
628 int
629 unsetenv(const char *name)
630 {
631 	int envNdx;
632 	size_t nameLen;
633 
634 	/* Check for malformed name. */
635 	if (name == NULL || (nameLen = __strleneq(name)) == 0) {
636 		errno = EINVAL;
637 		return (-1);
638 	}
639 
640 	/* Initialize environment. */
641 	if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
642 		return (-1);
643 
644 	/* Deactivate specified variable. */
645 	envNdx = envVarsTotal - 1;
646 	if (__findenv(name, nameLen, &envNdx, true) != NULL) {
647 		envVars[envNdx].active = false;
648 		if (envVars[envNdx].putenv)
649 			__remove_putenv(envNdx);
650 		__rebuild_environ(envActive - 1);
651 	}
652 
653 	return (0);
654 }
655