2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2007-2009 Sean C. Farley <scf@FreeBSD.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer,
12 * without modification, immediately at the beginning of the file.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
33 #include "namespace.h"
34 #include <sys/types.h>
41 #include "un-namespace.h"
42 #include "libc_private.h"
44 static const char CorruptEnvFindMsg[] = "environment corrupt; unable to find ";
45 static const char CorruptEnvValueMsg[] =
46 "environment corrupt; missing value for ";
50 * Standard environ. environ variable is exposed to entire process.
52 * origEnviron: Upon cleanup on unloading of library or failure, this
53 * allows environ to return to as it was before.
54 * environSize: Number of variables environ can hold. Can only
56 * intEnviron: Internally-built environ. Exposed via environ during
57 * (re)builds of the environment.
59 static char **origEnviron;
60 static char **intEnviron = NULL;
61 static int environSize = 0;
64 * Array of environment variables built from environ. Each element records:
65 * name: Pointer to name=value string
66 * name length: Length of name not counting '=' character
67 * value: Pointer to value within same string as name
68 * value size: Size (not length) of space for value not counting the
70 * active state: true/false value to signify whether variable is active.
71 * Useful since multiple variables with the same name can
72 * co-exist. At most, one variable can be active at any
74 * putenv: Created from putenv() call. This memory must not be
77 static struct envVars {
87 * Environment array information.
89 * envActive: Number of active variables in array.
90 * envVarsSize: Size of array.
91 * envVarsTotal: Number of total variables in array (active or not).
93 static int envActive = 0;
94 static int envVarsSize = 0;
95 static int envVarsTotal = 0;
98 /* Deinitialization of new environment. */
99 static void __attribute__ ((destructor)) __clean_env_destructor(void);
103 * A simple version of warnx() to avoid the bloat of including stdio in static
107 __env_warnx(const char *msg, const char *name, size_t nameLen)
109 static const char nl[] = "\n";
110 static const char progSep[] = ": ";
112 _write(STDERR_FILENO, _getprogname(), strlen(_getprogname()));
113 _write(STDERR_FILENO, progSep, sizeof(progSep) - 1);
114 _write(STDERR_FILENO, msg, strlen(msg));
115 _write(STDERR_FILENO, name, nameLen);
116 _write(STDERR_FILENO, nl, sizeof(nl) - 1);
123 * Inline strlen() for performance. Also, perform check for an equals sign.
124 * Cheaper here than performing a strchr() later.
127 __strleneq(const char *str)
131 for (s = str; *s != '\0'; ++s)
140 * Comparison of an environment name=value to a name.
143 strncmpeq(const char *nameValue, const char *name, size_t nameLen)
145 if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=')
153 * Using environment, returns pointer to value associated with name, if any,
154 * else NULL. If the onlyActive flag is set to true, only variables that are
155 * active are returned else all are.
158 __findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive)
163 * Find environment variable from end of array (more likely to be
164 * active). A variable created by putenv is always active, or it is not
165 * tracked in the array.
167 for (ndx = *envNdx; ndx >= 0; ndx--)
168 if (envVars[ndx].putenv) {
169 if (strncmpeq(envVars[ndx].name, name, nameLen)) {
171 return (envVars[ndx].name + nameLen +
174 } else if ((!onlyActive || envVars[ndx].active) &&
175 (envVars[ndx].nameLen == nameLen &&
176 strncmpeq(envVars[ndx].name, name, nameLen))) {
178 return (envVars[ndx].value);
186 * Using environ, returns pointer to value associated with name, if any, else
187 * NULL. Used on the original environ passed into the program.
190 __findenv_environ(const char *name, size_t nameLen)
194 /* Find variable within environ. */
195 for (envNdx = 0; environ[envNdx] != NULL; envNdx++)
196 if (strncmpeq(environ[envNdx], name, nameLen))
197 return (&(environ[envNdx][nameLen + sizeof("=") - 1]));
204 * Remove variable added by putenv() from variable tracking array.
207 __remove_putenv(int envNdx)
210 if (envVarsTotal > envNdx)
211 memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]),
212 (envVarsTotal - envNdx) * sizeof (*envVars));
213 memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars));
220 * Deallocate the environment built from environ as well as environ then set
221 * both to NULL. Eases debugging of memory leaks.
224 __clean_env(bool freeVars)
228 /* Deallocate environment and environ if created by *env(). */
229 if (envVars != NULL) {
230 for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--)
231 /* Free variables or deactivate them. */
232 if (envVars[envNdx].putenv) {
234 __remove_putenv(envNdx);
237 free(envVars[envNdx].name);
239 envVars[envNdx].active = false;
247 /* Restore original environ if it has not updated by program. */
248 if (origEnviron != NULL) {
249 if (environ == intEnviron)
250 environ = origEnviron;
262 * Using the environment, rebuild the environ array for use by other C library
263 * calls that depend upon it.
266 __rebuild_environ(int newEnvironSize)
273 /* Resize environ. */
274 if (newEnvironSize > environSize) {
275 tmpEnvironSize = newEnvironSize * 2;
276 tmpEnviron = reallocarray(intEnviron, tmpEnvironSize + 1,
277 sizeof(*intEnviron));
278 if (tmpEnviron == NULL)
280 environSize = tmpEnvironSize;
281 intEnviron = tmpEnviron;
283 envActive = newEnvironSize;
285 /* Assign active variables to environ. */
286 for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--)
287 if (envVars[envNdx].active)
288 intEnviron[environNdx++] = envVars[envNdx].name;
289 intEnviron[environNdx] = NULL;
291 /* Always set environ which may have been replaced by program. */
292 environ = intEnviron;
299 * Enlarge new environment.
305 struct envVars *tmpEnvVars;
308 if (envVarsTotal > envVarsSize) {
309 newEnvVarsSize = envVarsTotal * 2;
310 tmpEnvVars = reallocarray(envVars, newEnvVarsSize,
312 if (tmpEnvVars == NULL) {
316 envVarsSize = newEnvVarsSize;
317 envVars = tmpEnvVars;
325 * Using environ, build an environment for use by standard C library calls.
336 /* Check for non-existant environment. */
337 if (environ == NULL || environ[0] == NULL)
340 /* Count environment variables. */
341 for (env = environ, envVarsTotal = 0; *env != NULL; env++)
343 envVarsSize = envVarsTotal * 2;
345 /* Create new environment. */
346 envVars = calloc(envVarsSize, sizeof(*envVars));
350 /* Copy environ values and keep track of them. */
351 for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) {
352 envVars[envNdx].putenv = false;
353 envVars[envNdx].name =
354 strdup(environ[envVarsTotal - envNdx - 1]);
355 if (envVars[envNdx].name == NULL)
357 envVars[envNdx].value = strchr(envVars[envNdx].name, '=');
358 if (envVars[envNdx].value != NULL) {
359 envVars[envNdx].value++;
360 envVars[envNdx].valueSize =
361 strlen(envVars[envNdx].value);
363 __env_warnx(CorruptEnvValueMsg, envVars[envNdx].name,
364 strlen(envVars[envNdx].name));
370 * Find most current version of variable to make active. This
371 * will prevent multiple active variables from being created
372 * during this initialization phase.
374 nameLen = envVars[envNdx].value - envVars[envNdx].name - 1;
375 envVars[envNdx].nameLen = nameLen;
376 activeNdx = envVarsTotal - 1;
377 if (__findenv(envVars[envNdx].name, nameLen, &activeNdx,
379 __env_warnx(CorruptEnvFindMsg, envVars[envNdx].name,
384 envVars[activeNdx].active = true;
387 /* Create a new environ. */
388 origEnviron = environ;
390 if (__rebuild_environ(envVarsTotal) == 0)
403 * Destructor function with default argument to __clean_env().
406 __clean_env_destructor(void)
415 * Returns the value of a variable or NULL if none are found.
418 getenv(const char *name)
423 /* Check for malformed name. */
424 if (name == NULL || (nameLen = __strleneq(name)) == 0) {
430 * Variable search order:
431 * 1. Check for an empty environ. This allows an application to clear
433 * 2. Search the external environ array.
434 * 3. Search the internal environment.
436 * Since malloc() depends upon getenv(), getenv() must never cause the
437 * internal environment storage to be generated.
439 if (environ == NULL || environ[0] == NULL)
441 else if (envVars == NULL || environ != intEnviron)
442 return (__findenv_environ(name, nameLen));
444 envNdx = envVarsTotal - 1;
445 return (__findenv(name, nameLen, &envNdx, true));
451 * Runs getenv() unless the current process is tainted by uid or gid changes, in
452 * which case it will return NULL.
455 secure_getenv(const char *name)
459 return (getenv(name));
463 * Set the value of a variable. Older settings are labeled as inactive. If an
464 * older setting has enough room to store the new value, it will be reused. No
465 * previous variables are ever freed here to avoid causing a segmentation fault
468 * The variables nameLen and valueLen are passed into here to allow the caller
469 * to calculate the length by means besides just strlen().
472 __setenv(const char *name, size_t nameLen, const char *value, int overwrite)
480 /* Find existing environment variable large enough to use. */
481 envNdx = envVarsTotal - 1;
482 newEnvActive = envActive;
483 valueLen = strlen(value);
485 if (__findenv(name, nameLen, &envNdx, false) != NULL) {
486 /* Deactivate entry if overwrite is allowed. */
487 if (envVars[envNdx].active) {
490 envVars[envNdx].active = false;
494 /* putenv() created variable cannot be reused. */
495 if (envVars[envNdx].putenv)
496 __remove_putenv(envNdx);
498 /* Entry is large enough to reuse. */
499 else if (envVars[envNdx].valueSize >= valueLen)
503 /* Create new variable if none was found of sufficient size. */
505 /* Enlarge environment. */
506 envNdx = envVarsTotal;
507 if (!__enlarge_env())
510 /* Create environment entry. */
511 envVars[envNdx].name = malloc(nameLen + sizeof ("=") +
513 if (envVars[envNdx].name == NULL) {
517 envVars[envNdx].nameLen = nameLen;
518 envVars[envNdx].valueSize = valueLen;
520 /* Save name of name/value pair. */
521 env = stpncpy(envVars[envNdx].name, name, nameLen);
525 env = envVars[envNdx].value;
527 /* Save value of name/value pair. */
529 envVars[envNdx].value = env;
530 envVars[envNdx].active = true;
533 /* No need to rebuild environ if an active variable was reused. */
534 if (reuse && newEnvActive == envActive)
537 return (__rebuild_environ(newEnvActive));
542 * If the program attempts to replace the array of environment variables
543 * (environ) environ or sets the first varible to NULL, then deactivate all
544 * variables and merge in the new list from environ.
547 __merge_environ(void)
553 * Internally-built environ has been replaced or cleared (detected by
554 * using the count of active variables against a NULL as the first value
555 * in environ). Clean up everything.
557 if (intEnviron != NULL && (environ != intEnviron || (envActive > 0 &&
558 environ[0] == NULL))) {
559 /* Deactivate all environment variables. */
566 * Insert new environ into existing, yet deactivated,
569 origEnviron = environ;
570 if (origEnviron != NULL)
571 for (env = origEnviron; *env != NULL; env++) {
572 if ((equals = strchr(*env, '=')) == NULL) {
573 __env_warnx(CorruptEnvValueMsg, *env,
578 if (__setenv(*env, equals - *env, equals + 1,
589 * The exposed setenv() that performs a few tests before calling the function
590 * (__setenv()) that does the actual work of inserting a variable into the
594 setenv(const char *name, const char *value, int overwrite)
598 /* Check for malformed name. */
599 if (name == NULL || (nameLen = __strleneq(name)) == 0) {
604 /* Initialize environment. */
605 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
608 return (__setenv(name, nameLen, value, overwrite));
613 * Insert a "name=value" string into the environment. Special settings must be
614 * made to keep setenv() from reusing this memory block and unsetenv() from
615 * allowing it to be tracked.
625 /* Check for malformed argument. */
626 if (string == NULL || (equals = strchr(string, '=')) == NULL ||
627 (nameLen = equals - string) == 0) {
632 /* Initialize environment. */
633 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
636 /* Deactivate previous environment variable. */
637 envNdx = envVarsTotal - 1;
638 newEnvActive = envActive;
639 if (__findenv(string, nameLen, &envNdx, true) != NULL) {
640 /* Reuse previous putenv slot. */
641 if (envVars[envNdx].putenv) {
642 envVars[envNdx].name = string;
643 return (__rebuild_environ(envActive));
646 envVars[envNdx].active = false;
650 /* Enlarge environment. */
651 envNdx = envVarsTotal;
652 if (!__enlarge_env())
655 /* Create environment entry. */
656 envVars[envNdx].name = string;
657 envVars[envNdx].nameLen = -1;
658 envVars[envNdx].value = NULL;
659 envVars[envNdx].valueSize = -1;
660 envVars[envNdx].putenv = true;
661 envVars[envNdx].active = true;
664 return (__rebuild_environ(newEnvActive));
669 * Unset variable with the same name by flagging it as inactive. No variable is
673 unsetenv(const char *name)
679 /* Check for malformed name. */
680 if (name == NULL || (nameLen = __strleneq(name)) == 0) {
685 /* Initialize environment. */
686 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
689 /* Deactivate specified variable. */
690 /* Remove all occurrences. */
691 envNdx = envVarsTotal - 1;
692 newEnvActive = envActive;
693 while (__findenv(name, nameLen, &envNdx, true) != NULL) {
694 envVars[envNdx].active = false;
695 if (envVars[envNdx].putenv)
696 __remove_putenv(envNdx);
700 if (newEnvActive != envActive)
701 __rebuild_environ(newEnvActive);
707 * Unset all variable by flagging them as inactive. No variable is
715 /* Initialize environment. */
716 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
719 /* Remove from the end to not shuffle memory too much. */
720 for (ndx = envVarsTotal - 1; ndx >= 0; ndx--) {
721 envVars[ndx].active = false;
722 if (envVars[ndx].putenv)
723 __remove_putenv(ndx);
726 __rebuild_environ(0);