Index: lib/libc/stdlib/getenv.c =================================================================== RCS file: /home/ncvs/src/lib/libc/stdlib/getenv.c,v retrieving revision 1.9 diff -u -r1.9 getenv.c --- lib/libc/stdlib/getenv.c 4 Jul 2007 00:00:39 -0000 1.9 +++ lib/libc/stdlib/getenv.c 14 Jul 2007 00:59:51 -0000 @@ -36,6 +36,12 @@ __FBSDID("$FreeBSD: src/lib/libc/stdlib/getenv.c,v 1.9 2007/07/04 00:00:39 scf Exp $"); +static const char CorruptEnvFindMsg[] = + "environment corrupt; unable to find %.*s"; +static const char CorruptEnvValueMsg[] = + "environment corrupt; missing value for %s"; + + /* * Standard environ. environ variable is exposed to entire process. * @@ -43,9 +49,11 @@ * allows environ to return to as it was before. * environSize: Number of variables environ can hold. Can only * increase. + * watchEnviron: Pointer to watch for changes by program to environ. */ extern char **environ; static char **origEnviron; +static char **watchEnviron = NULL; static int environSize = 0; /* @@ -84,7 +92,7 @@ /* Deinitialization of new environment. */ -static void __attribute__ ((destructor)) __clean_env(void); +static void __attribute__ ((destructor)) __clean_env_destructor(void); /* @@ -173,6 +181,64 @@ /* + * Remove variable added by putenv() from variable tracking array. + */ +static void +__remove_putenv(int envNdx) +{ + envVarsTotal--; + if (envVarsTotal > envNdx) + memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]), + (envVarsTotal - envNdx) * sizeof (*envVars)); + memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars)); + + return; +} + + +/* + * Deallocate the environment built from environ as well as environ then set + * both to NULL. Eases debugging of memory leaks. + */ +static void +__clean_env(bool freeVars) +{ + int envNdx; + + /* Deallocate environment and environ if created by *env(). */ + if (envVars != NULL) { + for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) + /* Free variables or deactivate them. */ + if (envVars[envNdx].putenv) { + if (!freeVars) + __remove_putenv(envNdx); + } else { + if (freeVars) + free(envVars[envNdx].name); + else + envVars[envNdx].active = false; + } + if (freeVars) { + free(envVars); + envVars = NULL; + } else + envActive = 0; + + /* Restore original environ if it has not updated by program. */ + if (origEnviron != NULL) { + if (watchEnviron == environ) + environ = origEnviron; + free(watchEnviron); + watchEnviron = NULL; + environSize = 0; + } + } + + return; +} + + +/* * Using the environment, rebuild the environ array for use by other C library * calls that depend upon it. */ @@ -193,6 +259,7 @@ return (-1); environSize = tmpEnvironSize; environ = tmpEnviron; + watchEnviron = environ; } envActive = newEnvironSize; @@ -246,10 +313,8 @@ size_t nameLen; /* Check for non-existant environment. */ - if (environ == NULL) + if (environ == NULL || environ[0] == NULL) return (0); - if (environ[0] == NULL) - goto SaveEnviron; /* Count environment variables. */ for (env = environ, envVarsTotal = 0; *env != NULL; env++) @@ -274,8 +339,7 @@ envVars[envNdx].valueSize = strlen(envVars[envNdx].value); } else { - warnx("environment corrupt; missing value for %s", - envVars[envNdx].name); + warnx(CorruptEnvValueMsg, envVars[envNdx].name); errno = EFAULT; goto Failure; } @@ -290,8 +354,7 @@ activeNdx = envVarsTotal - 1; if (__findenv(envVars[envNdx].name, nameLen, &activeNdx, false) == NULL) { - warnx("environment corrupt; unable to find %.*s", - nameLen, envVars[envNdx].name); + warnx(CorruptEnvFindMsg, nameLen, envVars[envNdx].name); errno = EFAULT; goto Failure; } @@ -299,24 +362,20 @@ } /* Create a new environ. */ -SaveEnviron: origEnviron = environ; environ = NULL; - if (envVarsTotal > 0) { - rtrnVal = __rebuild_environ(envVarsTotal); - if (rtrnVal == -1) { - savedErrno = errno; - __clean_env(); - errno = savedErrno; - } - } else - rtrnVal = 0; + rtrnVal = __rebuild_environ(envVarsTotal); + if (rtrnVal == -1) { + savedErrno = errno; + __clean_env(true); + errno = savedErrno; + } return (rtrnVal); Failure: savedErrno = errno; - __clean_env(); + __clean_env(true); errno = savedErrno; return (-1); @@ -324,42 +383,12 @@ /* - * Remove variable added by putenv() from variable tracking array. + * Destructor function with default argument to __clean_env(). */ static void -__remove_putenv(int envNdx) +__clean_env_destructor(void) { - memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]), - (envVarsTotal - envNdx) * sizeof (*envVars)); - envVarsTotal--; - - return; -} - - -/* - * Deallocate the environment built from environ as well as environ then set - * both to NULL. Eases debugging of memory leaks. - */ -static void -__clean_env(void) -{ - int envNdx; - - /* Deallocate environment and environ if created by *env(). */ - if (envVars != NULL) { - for (envNdx = 0; envNdx < envVarsTotal; envNdx++) - if (!envVars[envNdx].putenv) - free(envVars[envNdx].name); - free(envVars); - envVars = NULL; - - /* Restore original environ. */ - if (origEnviron != NULL) { - free(environ); - environ = origEnviron; - } - } + __clean_env(true); return; } @@ -380,8 +409,12 @@ return (NULL); } - /* Find environment variable via environ or rebuilt environment. */ - if (envVars == NULL) + /* + * Find environment variable via environ if no changes have been made + * via a *env() call or environ has been replaced by a running program, + * otherwise, use the rebuilt environment. + */ + if (envVars == NULL || environ != watchEnviron) return (__findenv_environ(name, nameLen)); else { envNdx = envVarsTotal - 1; @@ -395,27 +428,19 @@ * older setting has enough room to store the new value, it will be reused. No * previous variables are ever freed here to avoid causing a segmentation fault * in a user's code. + * + * The variables nameLen and valueLen are passed into here to allow the caller + * to calculate the length by means besides just strlen(). */ -int -setenv(const char *name, const char *value, int overwrite) +static int +__setenv(const char *name, size_t nameLen, const char *value, int overwrite) { bool reuse; char *env; int envNdx; int newEnvActive; - size_t nameLen; size_t valueLen; - /* Check for malformed name. */ - if (name == NULL || (nameLen = __strleneq(name)) == 0) { - errno = EINVAL; - return (-1); - } - - /* Initialize environment. */ - if (envVars == NULL && __build_env() == -1) - return (-1); - /* Find existing environment variable large enough to use. */ envNdx = envVarsTotal - 1; newEnvActive = envActive; @@ -437,7 +462,6 @@ /* Entry is large enough to reuse. */ else if (envVars[envNdx].valueSize >= valueLen) reuse = true; - } /* Create new variable if none was found of sufficient size. */ @@ -480,7 +504,71 @@ /* - * Insert a "name=value" string into then environment. Special settings must be + * If the program attempts to replace the array of environment variables + * (environ) environ, then deactivate all variables and merge in the new list + * from environ. + */ +static int +__merge_environ(void) +{ + char **env; + char *equals; + + /* environ has been replaced. clean up everything. */ + if (envVarsTotal > 0 && environ != watchEnviron) { + /* Deactivate all environment variables. */ + origEnviron = NULL; + __clean_env(false); + + /* + * Insert new environ into existing, yet deactivated, + * environment array. + */ + origEnviron = environ; + environ = watchEnviron; + if (origEnviron != NULL) + for (env = origEnviron; *env != NULL; env++) { + if ((equals = strchr(*env, '=')) == NULL) { + warnx(CorruptEnvValueMsg, *env); + errno = EFAULT; + return (-1); + } + if (__setenv(*env, equals - *env, equals + 1, + 1) == -1) + return (-1); + } + } + + return (0); +} + + +/* + * The exposed setenv() that peforms a few tests before calling the function + * (__setenv()) that does the actual work of inserting a variable into the + * environment. + */ +int +setenv(const char *name, const char *value, int overwrite) +{ + size_t nameLen; + + /* Check for malformed name. */ + if (name == NULL || (nameLen = __strleneq(name)) == 0) { + errno = EINVAL; + return (-1); + } + + /* Initialize environment. */ + if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) + return (-1); + + return (__setenv(name, nameLen, value, overwrite)); +} + + +/* + * Insert a "name=value" string into the environment. Special settings must be * made to keep setenv() from reusing this memory block and unsetenv() from * allowing it to be tracked. */ @@ -500,7 +588,7 @@ } /* Initialize environment. */ - if (envVars == NULL && __build_env() == -1) + if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) return (-1); /* Deactivate previous environment variable. */ @@ -552,7 +640,7 @@ } /* Initialize environment. */ - if (envVars == NULL && __build_env() == -1) + if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) return (-1); /* Deactivate specified variable. */ cvs diff: Diffing tools/regression/environ Index: tools/regression/environ/envctl.c =================================================================== RCS file: /home/ncvs/src/tools/regression/environ/envctl.c,v retrieving revision 1.1 diff -u -r1.1 envctl.c --- tools/regression/environ/envctl.c 4 Jul 2007 00:00:39 -0000 1.1 +++ tools/regression/environ/envctl.c 14 Jul 2007 01:00:11 -0000 @@ -54,17 +54,19 @@ static void usage(const char *program) { - fprintf(stderr, "Usage: %s [-DGUcht] [-gu name] [-p name=value] " + fprintf(stderr, "Usage: %s [-CDGUchrt] [-gu name] [-p name=value] " "[(-S|-s name) value overwrite]\n\n" "Options:\n" + " -C\t\t\t\tClear environ variable with NULL pointer\n" " -D\t\t\t\tDump environ\n" " -G name\t\t\tgetenv(NULL)\n" " -S value overwrite\t\tsetenv(NULL, value, overwrite)\n" " -U\t\t\t\tunsetenv(NULL)\n" - " -c\t\t\t\tClear environ variable\n" + " -c\t\t\t\tClear environ variable with calloc()'d memory\n" " -g name\t\t\tgetenv(name)\n" " -h\t\t\t\tHelp\n" " -p name=value\t\t\tputenv(name=value)\n" + " -r\t\t\t\treplace environ with { \"FOO=bar\", NULL }\n" " -s name value overwrite\tsetenv(name, value, overwrite)\n" " -t\t\t\t\tOutput is suitable for testing (no newlines)\n" " -u name\t\t\tunsetenv(name)\n", @@ -77,7 +79,7 @@ int main(int argc, char **argv) { - char *cleanEnv[] = { NULL }; + char *staticEnv[] = { "FOO=bar", NULL }; char arg; const char *eol = "\n"; const char *value; @@ -87,15 +89,19 @@ exit(EXIT_FAILURE); } - while ((arg = getopt(argc, argv, "DGS:Ucg:hp:s:tu:")) != -1) { + while ((arg = getopt(argc, argv, "DGS:Ucg:hp:rs:tu:")) != -1) { switch (arg) { - case 'D': - errno = 0; - dump_environ(); + case 'C': + environ = NULL; break; case 'c': - environ = cleanEnv; + environ = calloc(1, sizeof(*environ)); + break; + + case 'D': + errno = 0; + dump_environ(); break; case 'G': @@ -113,6 +119,10 @@ printf("%d %d%s", putenv(optarg), errno, eol); break; + case 'r': + environ = staticEnv; + break; + case 'S': errno = 0; printf("%d %d%s", setenv(NULL, optarg, Index: tools/regression/environ/envtest.t =================================================================== RCS file: /home/ncvs/src/tools/regression/environ/envtest.t,v retrieving revision 1.1 diff -u -r1.1 envtest.t --- tools/regression/environ/envtest.t 4 Jul 2007 00:00:39 -0000 1.1 +++ tools/regression/environ/envtest.t 14 Jul 2007 01:00:11 -0000 @@ -132,6 +132,9 @@ -g FOO check_result "0 0 0 0 0 0 0 0 ${BAR}" +run_test -c -s FOO ${BAR} 1 -g FOO -c -s FOO ${NEWBAR} 1 -g FOO +check_result "0 0 ${BAR} 0 0 ${NEWBAR}" + # Unsets. run_test -u FOO -g FOO @@ -152,6 +155,10 @@ run_test -c -s FOO ${NEWBAR} 1 -g FOO -u FOO -g FOO check_result "0 0 ${NEWBAR} 0 0" +run_test -c -u FOO -s FOO ${BAR} 1 -g FOO -u FOO -g FOO -c -u FOO\ + -s FOO ${NEWBAR} 1 -g FOO +check_result "0 0 0 0 ${BAR} 0 0 0 0 0 0 ${NEWBAR}" + # Puts. run_test -p FOO=${NEWBAR} -g FOO @@ -180,3 +187,17 @@ run_test -s FOO ${NEWBAR} 1 -p FOO=${BAR} -u FOO check_result "0 0 0 0 0 0" + +run_test -s FOO ${NEWBAR} 1 -p FOO=${BAR} -c -g FOO -p FOO=${NEWBAR} -g FOO +check_result "0 0 0 0 0 0 ${NEWBAR}" + +run_test -c -p FOO=${BAR} -g FOO -c -p FOO=${NEWBAR} -g FOO +check_result "0 0 ${BAR} 0 0 ${NEWBAR}" + + +# environ replacements. +run_test -r -g FOO -s FOO ${BAR} 1 -g FOO -u FOO -g FOO +check_result "${BAR} 0 0 ${BAR} 0 0" + +run_test -r -g FOO -u FOO -g FOO -s FOO ${BAR} 1 -g FOO +check_result "${BAR} 0 0 0 0 ${BAR}" Index: tools/regression/environ/timings.c =================================================================== RCS file: /home/ncvs/src/tools/regression/environ/timings.c,v retrieving revision 1.1 diff -u -r1.1 timings.c --- tools/regression/environ/timings.c 4 Jul 2007 00:00:39 -0000 1.1 +++ tools/regression/environ/timings.c 14 Jul 2007 01:00:11 -0000 @@ -23,7 +23,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ +#include #include +#include #include #include #include @@ -64,43 +66,44 @@ main(int argc, char **argv) { int iterations; - struct timeval endTime; - struct timeval startTime; + struct rusage endUsage; + struct rusage startUsage; /* * getenv() on the existing environment. */ - gettimeofday(&startTime, NULL); + getrusage(RUSAGE_SELF, &startUsage); /* Iterate over setting variable. */ for (iterations = 0; iterations < MaxIterations; iterations++) if (getenv(name) == NULL) err(EXIT_FAILURE, "getenv(name)"); - gettimeofday(&endTime, NULL); + getrusage(RUSAGE_SELF, &endUsage); - report_time("getenv(name)", &startTime, &endTime); + report_time("getenv(name)", &startUsage.ru_utime, &endUsage.ru_utime); /* * setenv() a variable with a large value. */ - gettimeofday(&startTime, NULL); + getrusage(RUSAGE_SELF, &startUsage); /* Iterate over setting variable. */ for (iterations = 0; iterations < MaxIterations; iterations++) if (setenv(name, value1, 1) == -1) err(EXIT_FAILURE, "setenv(name, value1, 1)"); - gettimeofday(&endTime, NULL); + getrusage(RUSAGE_SELF, &endUsage); - report_time("setenv(name, value1, 1)", &startTime, &endTime); + report_time("setenv(name, value1, 1)", &startUsage.ru_utime, + &endUsage.ru_utime); /* * getenv() the new variable on the new environment. */ - gettimeofday(&startTime, NULL); + getrusage(RUSAGE_SELF, &startUsage); /* Iterate over setting variable. */ for (iterations = 0; iterations < MaxIterations; iterations++) @@ -108,15 +111,15 @@ if (getenv(name) == NULL) err(EXIT_FAILURE, "getenv(name)"); - gettimeofday(&endTime, NULL); + getrusage(RUSAGE_SELF, &endUsage); - report_time("getenv(name)", &startTime, &endTime); + report_time("getenv(name)", &startUsage.ru_utime, &endUsage.ru_utime); /* * getenv() a different variable on the new environment. */ - gettimeofday(&startTime, NULL); + getrusage(RUSAGE_SELF, &startUsage); /* Iterate over setting variable. */ for (iterations = 0; iterations < MaxIterations; iterations++) @@ -124,30 +127,31 @@ if (getenv(name2) == NULL) err(EXIT_FAILURE, "getenv(name2)"); - gettimeofday(&endTime, NULL); + getrusage(RUSAGE_SELF, &endUsage); - report_time("getenv(name2)", &startTime, &endTime); + report_time("getenv(name2)", &startUsage.ru_utime, &endUsage.ru_utime); /* * setenv() a variable with a small value. */ - gettimeofday(&startTime, NULL); + getrusage(RUSAGE_SELF, &startUsage); /* Iterate over setting variable. */ for (iterations = 0; iterations < MaxIterations; iterations++) if (setenv(name, value2, 1) == -1) err(EXIT_FAILURE, "setenv(name, value2, 1)"); - gettimeofday(&endTime, NULL); + getrusage(RUSAGE_SELF, &endUsage); - report_time("setenv(name, value2, 1)", &startTime, &endTime); + report_time("setenv(name, value2, 1)", &startUsage.ru_utime, + &endUsage.ru_utime); /* * getenv() a different variable on the new environment. */ - gettimeofday(&startTime, NULL); + getrusage(RUSAGE_SELF, &startUsage); /* Iterate over setting variable. */ for (iterations = 0; iterations < MaxIterations; iterations++) @@ -155,15 +159,15 @@ if (getenv(name2) == NULL) err(EXIT_FAILURE, "getenv(name)"); - gettimeofday(&endTime, NULL); + getrusage(RUSAGE_SELF, &endUsage); - report_time("getenv(name)", &startTime, &endTime); + report_time("getenv(name)", &startUsage.ru_utime, &endUsage.ru_utime); /* * getenv() a different variable on the new environment. */ - gettimeofday(&startTime, NULL); + getrusage(RUSAGE_SELF, &startUsage); /* Iterate over setting variable. */ for (iterations = 0; iterations < MaxIterations; iterations++) @@ -171,24 +175,25 @@ if (getenv(name2) == NULL) err(EXIT_FAILURE, "getenv(name2)"); - gettimeofday(&endTime, NULL); + getrusage(RUSAGE_SELF, &endUsage); - report_time("getenv(name2)", &startTime, &endTime); + report_time("getenv(name2)", &startUsage.ru_utime, &endUsage.ru_utime); /* * putenv() a variable with a small value. */ - gettimeofday(&startTime, NULL); + getrusage(RUSAGE_SELF, &startUsage); /* Iterate over setting variable. */ for (iterations = 0; iterations < MaxIterations; iterations++) if (putenv(nameValuePair) == -1) err(EXIT_FAILURE, "putenv(nameValuePair)"); - gettimeofday(&endTime, NULL); + getrusage(RUSAGE_SELF, &endUsage); - report_time("putenv(nameValuePair)", &startTime, &endTime); + report_time("putenv(nameValuePair)", &startUsage.ru_utime, + &endUsage.ru_utime); exit(EXIT_SUCCESS);