2 * Copyright (c) 2007 Sean C. Farley <scf@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
26 #include <sys/types.h>
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
39 static const char CorruptEnvFindMsg[] =
40 "environment corrupt; unable to find %.*s";
41 static const char CorruptEnvValueMsg[] =
42 "environment corrupt; missing value for %s";
46 * Standard environ. environ variable is exposed to entire process.
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
52 * intEnviron: Internally-built environ. Exposed via environ during
53 * (re)builds of the environment.
55 extern char **environ;
56 static char **origEnviron;
57 static char **intEnviron = NULL;
58 static int environSize = 0;
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
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
71 * putenv: Created from putenv() call. This memory must not be
74 static struct envVars {
84 * Environment array information.
86 * envActive: Number of active variables in array.
87 * envVarsSize: Size of array.
88 * envVarsTotal: Number of total variables in array (active or not).
90 static int envActive = 0;
91 static int envVarsSize = 0;
92 static int envVarsTotal = 0;
95 /* Deinitialization of new environment. */
96 static void __attribute__ ((destructor)) __clean_env_destructor(void);
100 * Inline strlen() for performance. Also, perform check for an equals sign.
101 * Cheaper here than peforming a strchr() later.
104 __strleneq(const char *str)
108 for (s = str; *s != '\0'; ++s)
117 * Comparison of an environment name=value to a name.
120 strncmpeq(const char *nameValue, const char *name, size_t nameLen)
122 if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=')
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.
135 __findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive)
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.
144 for (ndx = *envNdx; ndx >= 0; ndx--)
145 if (envVars[ndx].putenv) {
146 if (strncmpeq(envVars[ndx].name, name, nameLen)) {
148 return (envVars[ndx].name + nameLen +
151 } else if ((!onlyActive || envVars[ndx].active) &&
152 (envVars[ndx].nameLen == nameLen &&
153 strncmpeq(envVars[ndx].name, name, nameLen))) {
155 return (envVars[ndx].value);
163 * Using environ, returns pointer to value associated with name, if any, else
164 * NULL. Used on the original environ passed into the program.
167 __findenv_environ(const char *name, size_t nameLen)
171 /* Check for non-existant environment. */
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]));
185 * Remove variable added by putenv() from variable tracking array.
188 __remove_putenv(int envNdx)
191 if (envVarsTotal > envNdx)
192 memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]),
193 (envVarsTotal - envNdx) * sizeof (*envVars));
194 memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars));
201 * Deallocate the environment built from environ as well as environ then set
202 * both to NULL. Eases debugging of memory leaks.
205 __clean_env(bool freeVars)
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) {
215 __remove_putenv(envNdx);
218 free(envVars[envNdx].name);
220 envVars[envNdx].active = false;
228 /* Restore original environ if it has not updated by program. */
229 if (origEnviron != NULL) {
230 if (environ == intEnviron)
231 environ = origEnviron;
243 * Using the environment, rebuild the environ array for use by other C library
244 * calls that depend upon it.
247 __rebuild_environ(int newEnvironSize)
254 /* Resize environ. */
255 if (newEnvironSize > environSize) {
256 tmpEnvironSize = newEnvironSize * 2;
257 tmpEnviron = realloc(intEnviron, sizeof (*intEnviron) *
258 (tmpEnvironSize + 1));
259 if (tmpEnviron == NULL)
261 environSize = tmpEnvironSize;
262 intEnviron = tmpEnviron;
264 envActive = newEnvironSize;
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;
272 /* Always set environ which may have been replaced by program. */
273 environ = intEnviron;
280 * Enlarge new environment.
286 struct envVars *tmpEnvVars;
289 if (envVarsTotal > envVarsSize) {
290 newEnvVarsSize = envVarsTotal * 2;
291 tmpEnvVars = realloc(envVars, sizeof (*envVars) *
293 if (tmpEnvVars == NULL) {
297 envVarsSize = newEnvVarsSize;
298 envVars = tmpEnvVars;
306 * Using environ, build an environment for use by standard C library calls.
317 /* Check for non-existant environment. */
318 if (environ == NULL || environ[0] == NULL)
321 /* Count environment variables. */
322 for (env = environ, envVarsTotal = 0; *env != NULL; env++)
324 envVarsSize = envVarsTotal * 2;
326 /* Create new environment. */
327 envVars = calloc(1, sizeof (*envVars) * envVarsSize);
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)
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);
344 warnx(CorruptEnvValueMsg, envVars[envNdx].name);
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.
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,
359 warnx(CorruptEnvFindMsg, (int)nameLen,
360 envVars[envNdx].name);
364 envVars[activeNdx].active = true;
367 /* Create a new environ. */
368 origEnviron = environ;
370 if (__rebuild_environ(envVarsTotal) == 0)
383 * Destructor function with default argument to __clean_env().
386 __clean_env_destructor(void)
395 * Returns the value of a variable or NULL if none are found.
398 getenv(const char *name)
403 /* Check for malformed name. */
404 if (name == NULL || (nameLen = __strleneq(name)) == 0) {
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.
414 if (envVars == NULL || environ != intEnviron)
415 return (__findenv_environ(name, nameLen));
417 envNdx = envVarsTotal - 1;
418 return (__findenv(name, nameLen, &envNdx, true));
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
429 * The variables nameLen and valueLen are passed into here to allow the caller
430 * to calculate the length by means besides just strlen().
433 __setenv(const char *name, size_t nameLen, const char *value, int overwrite)
441 /* Find existing environment variable large enough to use. */
442 envNdx = envVarsTotal - 1;
443 newEnvActive = envActive;
444 valueLen = strlen(value);
446 if (__findenv(name, nameLen, &envNdx, false) != NULL) {
447 /* Deactivate entry if overwrite is allowed. */
448 if (envVars[envNdx].active) {
451 envVars[envNdx].active = false;
455 /* putenv() created variable cannot be reused. */
456 if (envVars[envNdx].putenv)
457 __remove_putenv(envNdx);
459 /* Entry is large enough to reuse. */
460 else if (envVars[envNdx].valueSize >= valueLen)
464 /* Create new variable if none was found of sufficient size. */
466 /* Enlarge environment. */
467 envNdx = envVarsTotal;
468 if (!__enlarge_env())
471 /* Create environment entry. */
472 envVars[envNdx].name = malloc(nameLen + sizeof ("=") +
474 if (envVars[envNdx].name == NULL) {
478 envVars[envNdx].nameLen = nameLen;
479 envVars[envNdx].valueSize = valueLen;
481 /* Save name of name/value pair. */
482 env = stpcpy(envVars[envNdx].name, name);
483 if ((envVars[envNdx].name)[nameLen] != '=')
484 env = stpcpy(env, "=");
487 env = envVars[envNdx].value;
489 /* Save value of name/value pair. */
491 envVars[envNdx].value = env;
492 envVars[envNdx].active = true;
495 /* No need to rebuild environ if an active variable was reused. */
496 if (reuse && newEnvActive == envActive)
499 return (__rebuild_environ(newEnvActive));
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
509 __merge_environ(void)
514 /* environ has been replaced. clean up everything. */
515 if (envVarsTotal > 0 && environ != intEnviron) {
516 /* Deactivate all environment variables. */
523 * Insert new environ into existing, yet deactivated,
526 origEnviron = environ;
527 if (origEnviron != NULL)
528 for (env = origEnviron; *env != NULL; env++) {
529 if ((equals = strchr(*env, '=')) == NULL) {
530 warnx(CorruptEnvValueMsg, *env);
534 if (__setenv(*env, equals - *env, equals + 1,
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
550 setenv(const char *name, const char *value, int overwrite)
554 /* Check for malformed name. */
555 if (name == NULL || (nameLen = __strleneq(name)) == 0) {
560 /* Initialize environment. */
561 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
564 return (__setenv(name, nameLen, value, overwrite));
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.
581 /* Check for malformed argument. */
582 if (string == NULL || (equals = strchr(string, '=')) == NULL ||
583 (nameLen = equals - string) == 0) {
588 /* Initialize environment. */
589 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
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));
602 envVars[envNdx].active = false;
606 /* Enlarge environment. */
607 envNdx = envVarsTotal;
608 if (!__enlarge_env())
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;
620 return (__rebuild_environ(newEnvActive));
625 * Unset variable with the same name by flagging it as inactive. No variable is
629 unsetenv(const char *name)
634 /* Check for malformed name. */
635 if (name == NULL || (nameLen = __strleneq(name)) == 0) {
640 /* Initialize environment. */
641 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
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);