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