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