diff --git a/bin/df/df.c b/bin/df/df.c index 5420da92500e..f48c8c95e841 100644 --- a/bin/df/df.c +++ b/bin/df/df.c @@ -131,14 +131,14 @@ main(int argc, char *argv[]) */ if (kflag) break; - putenv("BLOCKSIZE=512"); + setenv("BLOCKSIZE", "512", 1); hflag = 0; break; case 'c': cflag = 1; break; case 'g': - putenv("BLOCKSIZE=1g"); + setenv("BLOCKSIZE", "1g", 1); hflag = 0; break; case 'H': @@ -152,7 +152,7 @@ main(int argc, char *argv[]) break; case 'k': kflag++; - putenv("BLOCKSIZE=1024"); + setenv("BLOCKSIZE", "1024", 1); hflag = 0; break; case 'l': @@ -162,7 +162,7 @@ main(int argc, char *argv[]) lflag = 1; break; case 'm': - putenv("BLOCKSIZE=1m"); + setenv("BLOCKSIZE", "1m", 1); hflag = 0; break; case 'n': diff --git a/bin/sh/var.c b/bin/sh/var.c index 54a0a84080c3..afb3c862e653 100644 --- a/bin/sh/var.c +++ b/bin/sh/var.c @@ -289,6 +289,7 @@ void setvareq(char *s, int flags) { struct var *vp, **vpp; + char *p; int len; if (aflag) @@ -319,7 +320,10 @@ setvareq(char *s, int flags) if (vp == &vmpath || (vp == &vmail && ! mpathset())) chkmail(1); if ((vp->flags & VEXPORT) && localevar(s)) { - putenv(s); + p = strchr(s, '='); + *p = '\0'; + (void) setenv(s, p + 1, 1); + *p = '='; (void) setlocale(LC_ALL, ""); } INTON; @@ -335,7 +339,10 @@ setvareq(char *s, int flags) INTOFF; *vpp = vp; if ((vp->flags & VEXPORT) && localevar(s)) { - putenv(s); + p = strchr(s, '='); + *p = '\0'; + (void) setenv(s, p + 1, 1); + *p = '='; (void) setlocale(LC_ALL, ""); } INTON; @@ -596,7 +603,10 @@ exportcmd(int argc, char **argv) vp->flags |= flag; if ((vp->flags & VEXPORT) && localevar(vp->text)) { - putenv(vp->text); + p = strchr(vp->text, '='); + *p = '\0'; + (void) setenv(vp->text, p + 1, 1); + *p = '='; (void) setlocale(LC_ALL, ""); } goto found; diff --git a/include/stdlib.h b/include/stdlib.h index 98fe8b7756c6..98cb465c8f33 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -161,7 +161,7 @@ void _Exit(int) __dead2; int posix_memalign(void **, size_t, size_t); /* (ADV) */ int rand_r(unsigned *); /* (TSF) */ int setenv(const char *, const char *, int); -void unsetenv(const char *); +int unsetenv(const char *); #endif /* @@ -197,7 +197,7 @@ long mrand48(void); long nrand48(unsigned short[3]); int posix_openpt(int); char *ptsname(int); -int putenv(const char *); +int putenv(char *); long random(void); char *realpath(const char *, char resolved_path[]); unsigned short diff --git a/lib/libc/stdlib/Makefile.inc b/lib/libc/stdlib/Makefile.inc index 99ea7fb20ff3..f9b8fec7ab9d 100644 --- a/lib/libc/stdlib/Makefile.inc +++ b/lib/libc/stdlib/Makefile.inc @@ -8,10 +8,10 @@ MISRCS+=_Exit.c a64l.c abort.c abs.c atexit.c atof.c atoi.c atol.c atoll.c \ bsearch.c div.c exit.c getenv.c getopt.c getopt_long.c \ getsubopt.c grantpt.c hcreate.c heapsort.c imaxabs.c imaxdiv.c \ insque.c l64a.c labs.c ldiv.c llabs.c lldiv.c lsearch.c malloc.c \ - merge.c putenv.c qsort.c qsort_r.c radixsort.c rand.c random.c \ - reallocf.c realpath.c remque.c setenv.c strfmon.c strtoimax.c \ - strtol.c strtoll.c strtoq.c strtoul.c strtonum.c strtoull.c strtoumax.c \ - strtouq.c system.c tdelete.c tfind.c tsearch.c twalk.c + merge.c qsort.c qsort_r.c radixsort.c rand.c random.c \ + reallocf.c realpath.c remque.c strfmon.c strtoimax.c \ + strtol.c strtoll.c strtoq.c strtoul.c strtonum.c strtoull.c \ + strtoumax.c strtouq.c system.c tdelete.c tfind.c tsearch.c twalk.c SYM_MAPS+= ${.CURDIR}/stdlib/Symbol.map diff --git a/lib/libc/stdlib/getenv.3 b/lib/libc/stdlib/getenv.3 index 3d365f16a26b..33d9c36f3a79 100644 --- a/lib/libc/stdlib/getenv.3 +++ b/lib/libc/stdlib/getenv.3 @@ -32,7 +32,7 @@ .\" @(#)getenv.3 8.2 (Berkeley) 12/11/93 .\" $FreeBSD$ .\" -.Dd October 12, 2006 +.Dd June 20, 2007 .Dt GETENV 3 .Os .Sh NAME @@ -50,22 +50,13 @@ .Ft int .Fn setenv "const char *name" "const char *value" "int overwrite" .Ft int -.Fn putenv "const char *string" -.Ft void +.Fn putenv "char *string" +.Ft int .Fn unsetenv "const char *name" .Sh DESCRIPTION These functions set, unset and fetch environment variables from the host .Em environment list . -For compatibility with differing environment conventions, -the given arguments -.Fa name -and -.Fa value -may be appended and prepended, -respectively, -with an equal sign -.Dq Li \&= . .Pp The .Fn getenv @@ -97,11 +88,18 @@ to the given .Pp The .Fn putenv -function takes an argument of the form ``name=value'' and is -equivalent to: -.Bd -literal -offset indent -setenv(name, value, 1); -.Ed +function takes an argument of the form ``name=value'' and +puts it directly into the current environment, +so altering the argument shall change the environment. +If the variable +.Fa name +does not exist in the list, +it is inserted with the given +.Fa value . +If the variable +.Fa name +does exist, it is reset to the given +.Fa value . .Pp The .Fn unsetenv @@ -121,15 +119,55 @@ is not in the current environment, .Dv NULL is returned. .Pp -.Rv -std setenv putenv +.Rv -std setenv putenv unsetenv .Sh ERRORS .Bl -tag -width Er +.It Bq Er EINVAL +The function +.Fn getenv , +.Fn setenv +or +.Fn unsetenv +failed because the +.Fa name +is a +.Dv NULL +pointer, points to an empty string, or points to a string containing an +.Dq Li \&= +character. +.Pp +The function +.Fn putenv +failed because +.Fa string +is a +.Dv NULL +pointer, +.Fa string is without an +.Dq Li \&= +character or +.Dq Li \&= +is the first character in +.Fa string . +This does not follow the +.Tn POSIX +specification. .It Bq Er ENOMEM The function -.Fn setenv +.Fn setenv , +.Fn unsetenv or .Fn putenv failed because they were unable to allocate memory for the environment. +.It Bq Er EFAULT +The functions +.Fn setenv , +.Fn unsetenv +or +.Fn putenv +failed to make a valid copy of the environment due to the environment being +corrupt (i.e., a name without a value). A warning will be output to stderr with +information about the issue. .El .Sh SEE ALSO .Xr csh 1 , @@ -141,6 +179,13 @@ The .Fn getenv function conforms to .St -isoC . +The +.Fn setenv , +.Fn putenv +and +.Fn unsetenv +functions conforms to +.St -p1003.1-2001 . .Sh HISTORY The functions .Fn setenv @@ -152,19 +197,30 @@ The .Fn putenv function appeared in .Bx 4.3 Reno . +.Pp +Until +.Fx 7.0 , +.Fn putenv +would make a copy of +.Fa string +and insert it into the environment using +.Fn setenv . +This was changed to use +.Fa string +as the memory location of the ``name=value'' pair to follow the +.Tn POSIX +specification. .Sh BUGS Successive calls to .Fn setenv -or -.Fn putenv -assigning a differently sized +that assign a larger-sized .Fa value -to the same +than any previous value to the same .Fa name will result in a memory leak. The .Fx -semantics for these functions +semantics for this function (namely, that the contents of .Fa value are copied and that old values remain accessible indefinitely) make this diff --git a/lib/libc/stdlib/getenv.c b/lib/libc/stdlib/getenv.c index 306b6a19f8be..5f6f49797362 100644 --- a/lib/libc/stdlib/getenv.c +++ b/lib/libc/stdlib/getenv.c @@ -1,89 +1,568 @@ -/* - * Copyright (c) 1987, 1993 - * The Regents of the University of California. All rights reserved. +/*- + * Copyright (c) 2007 Sean C. Farley + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * notice, this list of conditions and the following disclaimer, + * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include +#include +#include +#include +#include +#include +#include + -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)getenv.c 8.1 (Berkeley) 6/4/93"; -#endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); -#include -#include -#include - -inline char *__findenv(const char *, int *); /* - * __findenv -- - * Returns pointer to value associated with name, if any, else NULL. - * Sets offset to be the offset of the name/value combination in the - * environmental array, for use by setenv(3) and unsetenv(3). - * Explicitly removes '=' in argument name. + * Standard environ. environ variable is exposed to entire process. * - * This routine *should* be a static; don't use it. + * origEnviron: Upon cleanup on unloading of library or failure, this + * allows environ to return to as it was before. + * environSize: Number of variables environ can hold. Can only + * increase. */ -inline char * -__findenv(name, offset) - const char *name; - int *offset; -{ - extern char **environ; - int len, i; - const char *np; - char **p, *cp; +extern char **environ; +static char **origEnviron; +static int environSize = 0; - if (name == NULL || environ == NULL) - return (NULL); - for (np = name; *np && *np != '='; ++np) - continue; - len = np - name; - for (p = environ; (cp = *p) != NULL; ++p) { - for (np = name, i = len; i && *cp; i--) - if (*cp++ != *np++) - break; - if (i == 0 && *cp++ == '=') { - *offset = p - environ; - return (cp); +/* + * Array of environment variables built from environ. Each element records: + * name: Pointer to name=value string + * name length: Length of name not counting '=' character + * value: Pointer to value within same string as name + * value size: Size (not length) of space for value not counting the + * nul character + * active state: true/false value to signify whether variable is active. + * Useful since multiple variables with the same name can + * co-exist. At most, one variable can be active at any + * one time. + * putenv: Created from putenv() call. This memory must not be + * reused. + */ +static struct envVars { + size_t nameLen; + size_t valueSize; + char *name; + char *value; + bool active; + bool putenv; +} *envVars = NULL; + +/* + * Environment array information. + * + * envActive: Number of active variables in array. + * envVarsSize: Size of array. + * envVarsTotal: Number of total variables in array (active or not). + */ +static int envActive = 0; +static int envVarsSize = 0; +static int envVarsTotal = 0; + + +/* Deinitialization of new environment. */ +static void __attribute__ ((destructor)) __clean_env(void); + + +/* + * Inline strlen() for performance. Also, perform check for an equals sign. + * Cheaper here than peforming a strchr() later. + */ +static inline size_t +__strleneq(const char *str) +{ + const char *s; + + for (s = str; *s != '\0'; ++s) + if (*s == '=') + return (0); + + return (s - str); +} + + +/* + * Comparison of an environment name=value to a name. + */ +static inline bool +strncmpeq(const char *nameValue, const char *name, size_t nameLen) +{ + if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=') + return (true); + + return (false); +} + + +/* + * Using environment, returns pointer to value associated with name, if any, + * else NULL. If the onlyActive flag is set to true, only variables that are + * active are returned else all are. + */ +static inline char * +__findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive) +{ + int ndx; + + /* + * Find environment variable from end of array (more likely to be + * active). A variable created by putenv is always active or it is not + * tracked in the array. + */ + for (ndx = *envNdx; ndx >= 0; ndx--) + if (envVars[ndx].putenv) { + if (strncmpeq(envVars[ndx].name, name, nameLen)) { + *envNdx = ndx; + return (envVars[ndx].name + nameLen + + sizeof ("=") - 1); + } + } else if ((!onlyActive || envVars[ndx].active) && + (envVars[ndx].nameLen == nameLen && + strncmpeq(envVars[ndx].name, name, nameLen))) { + *envNdx = ndx; + return (envVars[ndx].value); } - } + return (NULL); } + /* - * getenv -- - * Returns ptr to value associated with name, if any, else NULL. + * Using environ, returns pointer to value associated with name, if any, else + * NULL. Used on the original environ passed into the program. + */ +static char * +__findenv_environ(const char *name, size_t nameLen) +{ + int envNdx; + + /* Check for non-existant environment. */ + if (environ == NULL) + return (NULL); + + /* Find variable within environ. */ + for (envNdx = 0; environ[envNdx] != NULL; envNdx++) + if (strncmpeq(environ[envNdx], name, nameLen)) + return (&(environ[envNdx][nameLen + sizeof("=") - 1])); + + return (NULL); +} + + +/* + * Using the environment, rebuild the environ array for use by other C library + * calls that depend upon it. + */ +static int +__rebuild_environ(int newEnvironSize) +{ + char **tmpEnviron; + int envNdx; + int environNdx; + int tmpEnvironSize; + + /* Resize environ. */ + if (newEnvironSize > environSize) { + tmpEnvironSize = newEnvironSize * 2; + tmpEnviron = realloc(environ, sizeof (*environ) * + (tmpEnvironSize + 1)); + if (tmpEnviron == NULL) + return (-1); + environSize = tmpEnvironSize; + environ = tmpEnviron; + } + envActive = newEnvironSize; + + /* Assign active variables to environ. */ + for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--) + if (envVars[envNdx].active) + environ[environNdx++] = envVars[envNdx].name; + environ[environNdx] = NULL; + + return (0); +} + + +/* + * Enlarge new environment. + */ +static inline bool +__enlarge_env(void) +{ + int newEnvVarsSize; + struct envVars *tmpEnvVars; + + envVarsTotal++; + if (envVarsTotal > envVarsSize) { + newEnvVarsSize = envVarsTotal * 2; + tmpEnvVars = realloc(envVars, sizeof (*envVars) * + newEnvVarsSize); + if (tmpEnvVars == NULL) { + envVarsTotal--; + return (false); + } + envVarsSize = newEnvVarsSize; + envVars = tmpEnvVars; + } + + return (true); +} + + +/* + * Using environ, build an environment for use by standard C library calls. + */ +static int +__build_env(void) +{ + char **env; + int activeNdx; + int envNdx; + int rtrnVal; + int savedErrno; + size_t nameLen; + + /* Check for non-existant environment. */ + if (environ == NULL) + return (0); + if (environ[0] == NULL) + goto SaveEnviron; + + /* Count environment variables. */ + for (env = environ, envVarsTotal = 0; *env != NULL; env++) + envVarsTotal++; + envVarsSize = envVarsTotal * 2; + + /* Create new environment. */ + envVars = calloc(1, sizeof (*envVars) * envVarsSize); + if (envVars == NULL) + goto Failure; + + /* Copy environ values and keep track of them. */ + for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) { + envVars[envNdx].putenv = false; + envVars[envNdx].name = + strdup(environ[envVarsTotal - envNdx - 1]); + if (envVars[envNdx].name == NULL) + goto Failure; + envVars[envNdx].value = strchr(envVars[envNdx].name, '='); + if (envVars[envNdx].value != NULL) { + envVars[envNdx].value++; + envVars[envNdx].valueSize = + strlen(envVars[envNdx].value); + } else { + warnx("environment corrupt; missing value for %s", + envVars[envNdx].name); + errno = EFAULT; + goto Failure; + } + + /* + * Find most current version of variable to make active. This + * will prevent multiple active variables from being created + * during this initialization phase. + */ + nameLen = envVars[envNdx].value - envVars[envNdx].name - 1; + envVars[envNdx].nameLen = nameLen; + activeNdx = envVarsTotal - 1; + if (__findenv(envVars[envNdx].name, nameLen, &activeNdx, + false) == NULL) { + warnx("environment corrupt; unable to find %.*s", + nameLen, envVars[envNdx].name); + errno = EFAULT; + goto Failure; + } + envVars[activeNdx].active = true; + } + + /* 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; + + return (rtrnVal); + +Failure: + savedErrno = errno; + __clean_env(); + errno = savedErrno; + + return (-1); +} + + +/* + * Remove variable added by putenv() from variable tracking array. + */ +static void +__remove_putenv(int envNdx) +{ + 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; + } + } + + return; +} + + +/* + * Returns the value of a variable or NULL if none are found. */ char * -getenv(name) - const char *name; +getenv(const char *name) { - int offset; + int envNdx; + size_t nameLen; - return (__findenv(name, &offset)); + /* Check for malformed name. */ + if (name == NULL || (nameLen = __strleneq(name)) == 0) { + errno = EINVAL; + return (NULL); + } + + /* Find environment variable via environ or rebuilt environment. */ + if (envVars == NULL) + return (__findenv_environ(name, nameLen)); + else { + envNdx = envVarsTotal - 1; + return (__findenv(name, nameLen, &envNdx, true)); + } +} + + +/* + * Set the value of a variable. Older settings are labeled as inactive. If an + * 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. + */ +int +setenv(const char *name, 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; + valueLen = strlen(value); + reuse = false; + if (__findenv(name, nameLen, &envNdx, false) != NULL) { + /* Deactivate entry if overwrite is allowed. */ + if (envVars[envNdx].active) { + if (overwrite == 0) + return (0); + envVars[envNdx].active = false; + newEnvActive--; + } + + /* putenv() created variable cannot be reused. */ + if (envVars[envNdx].putenv) + __remove_putenv(envNdx); + + /* Entry is large enough to reuse. */ + else if (envVars[envNdx].valueSize >= valueLen) + reuse = true; + + } + + /* Create new variable if none was found of sufficient size. */ + if (! reuse) { + /* Enlarge environment. */ + envNdx = envVarsTotal; + if (!__enlarge_env()) + return (-1); + + /* Create environment entry. */ + envVars[envNdx].name = malloc(nameLen + sizeof ("=") + + valueLen); + if (envVars[envNdx].name == NULL) { + envVarsTotal--; + return (-1); + } + envVars[envNdx].nameLen = nameLen; + envVars[envNdx].valueSize = valueLen; + + /* Save name of name/value pair. */ + env = stpcpy(envVars[envNdx].name, name); + if ((envVars[envNdx].name)[nameLen] != '=') + env = stpcpy(env, "="); + } + else + env = envVars[envNdx].value; + + /* Save value of name/value pair. */ + strcpy(env, value); + envVars[envNdx].value = env; + envVars[envNdx].active = true; + newEnvActive++; + + /* No need to rebuild environ if the variable was reused. */ + if (reuse) + return (0); + else + return (__rebuild_environ(newEnvActive)); +} + + +/* + * Insert a "name=value" string into then environment. Special settings must be + * made to keep setenv() from reusing this memory block and unsetenv() from + * allowing it to be tracked. + */ +int +putenv(char *string) +{ + char *equals; + int envNdx; + int newEnvActive; + size_t nameLen; + + /* Check for malformed argument. */ + if (string == NULL || (equals = strchr(string, '=')) == NULL || + (nameLen = equals - string) == 0) { + errno = EINVAL; + return (-1); + } + + /* Initialize environment. */ + if (envVars == NULL && __build_env() == -1) + return (-1); + + /* Deactivate previous environment variable. */ + envNdx = envVarsTotal - 1; + newEnvActive = envActive; + if (__findenv(string, nameLen, &envNdx, true) != NULL) { + /* Reuse previous putenv slot. */ + if (envVars[envNdx].putenv) { + envVars[envNdx].name = string; + return (__rebuild_environ(envActive)); + } else { + newEnvActive--; + envVars[envNdx].active = false; + } + } + + /* Enlarge environment. */ + envNdx = envVarsTotal; + if (!__enlarge_env()) + return (-1); + + /* Create environment entry. */ + envVars[envNdx].name = string; + envVars[envNdx].nameLen = -1; + envVars[envNdx].value = NULL; + envVars[envNdx].valueSize = -1; + envVars[envNdx].putenv = true; + envVars[envNdx].active = true; + newEnvActive++; + + return (__rebuild_environ(newEnvActive)); +} + + +/* + * Unset variable with the same name by flagging it as inactive. No variable is + * ever freed. + */ +int +unsetenv(const char *name) +{ + int envNdx; + size_t nameLen; + + /* 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); + + /* Deactivate specified variable. */ + envNdx = envVarsTotal - 1; + if (__findenv(name, nameLen, &envNdx, true) != NULL) { + envVars[envNdx].active = false; + if (envVars[envNdx].putenv) + __remove_putenv(envNdx); + __rebuild_environ(envActive - 1); + } + + return (0); } diff --git a/lib/libc/stdlib/putenv.c b/lib/libc/stdlib/putenv.c deleted file mode 100644 index a5eea5d0ddfd..000000000000 --- a/lib/libc/stdlib/putenv.c +++ /dev/null @@ -1,56 +0,0 @@ -/*- - * Copyright (c) 1988, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)putenv.c 8.2 (Berkeley) 3/27/94"; -#endif /* LIBC_SCCS and not lint */ -#include -__FBSDID("$FreeBSD$"); - -#include -#include - -int -putenv(str) - const char *str; -{ - char *p, *equal; - int rval; - - if ((p = strdup(str)) == NULL) - return (-1); - if ((equal = index(p, '=')) == NULL) { - (void)free(p); - return (-1); - } - *equal = '\0'; - rval = setenv(p, equal + 1, 1); - (void)free(p); - return (rval); -} diff --git a/lib/libc/stdlib/setenv.c b/lib/libc/stdlib/setenv.c deleted file mode 100644 index 202c022ebfcf..000000000000 --- a/lib/libc/stdlib/setenv.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 1987, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)setenv.c 8.1 (Berkeley) 6/4/93"; -#endif /* LIBC_SCCS and not lint */ -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include - -char *__findenv(const char *, int *); - -/* - * setenv -- - * Set the value of the environmental variable "name" to be - * "value". If rewrite is set, replace any current value. - */ -int -setenv(name, value, rewrite) - const char *name; - const char *value; - int rewrite; -{ - extern char **environ; - static char **alloced; /* if allocated space before */ - char *c; - int l_value, offset; - - if (*value == '=') /* no `=' in value */ - ++value; - l_value = strlen(value); - if ((c = __findenv(name, &offset))) { /* find if already exists */ - if (!rewrite) - return (0); - if (strlen(c) >= l_value) { /* old larger; copy over */ - while ( (*c++ = *value++) ); - return (0); - } - } else { /* create new slot */ - int cnt; - char **p; - - for (p = environ, cnt = 0; *p; ++p, ++cnt); - if (alloced == environ) { /* just increase size */ - p = (char **)realloc((char *)environ, - (size_t)(sizeof(char *) * (cnt + 2))); - if (!p) - return (-1); - alloced = environ = p; - } - else { /* get new space */ - /* copy old entries into it */ - p = malloc((size_t)(sizeof(char *) * (cnt + 2))); - if (!p) - return (-1); - bcopy(environ, p, cnt * sizeof(char *)); - alloced = environ = p; - } - environ[cnt + 1] = NULL; - offset = cnt; - } - for (c = (char *)name; *c && *c != '='; ++c); /* no `=' in name */ - if (!(environ[offset] = /* name + `=' + value */ - malloc((size_t)((int)(c - name) + l_value + 2)))) - return (-1); - for (c = environ[offset]; (*c = *name++) && *c != '='; ++c); - for (*c++ = '='; (*c++ = *value++); ); - return (0); -} - -/* - * unsetenv(name) -- - * Delete environmental variable "name". - */ -void -unsetenv(name) - const char *name; -{ - extern char **environ; - char **p; - int offset; - - while (__findenv(name, &offset)) /* if set multiple times */ - for (p = &environ[offset];; ++p) - if (!(*p = *(p + 1))) - break; -} diff --git a/libexec/pppoed/pppoed.c b/libexec/pppoed/pppoed.c index 8dfebb70ff27..f07dd8c07d5d 100644 --- a/libexec/pppoed/pppoed.c +++ b/libexec/pppoed/pppoed.c @@ -258,7 +258,7 @@ Spawn(const char *prog, const char *acname, const char *provider, struct ng_mesg *rep = (struct ng_mesg *)msgbuf; struct ngpppoe_sts *sts = (struct ngpppoe_sts *)(msgbuf + sizeof *rep); struct ngpppoe_init_data *data; - char env[sizeof(HISMACADDR)+18], unknown[14], sessionid[5], *path; + char env[18], unknown[14], sessionid[5], *path; unsigned char *macaddr; const char *msg; int ret, slen; @@ -352,11 +352,11 @@ Spawn(const char *prog, const char *acname, const char *provider, /* Put the peer's MAC address in the environment */ if (sz >= sizeof(struct ether_header)) { macaddr = ((struct ether_header *)request)->ether_shost; - snprintf(env, sizeof(env), "%s=%x:%x:%x:%x:%x:%x", HISMACADDR, + snprintf(env, sizeof(env), "%x:%x:%x:%x:%x:%x", macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]); - if (putenv(env) != 0) - syslog(LOG_INFO, "putenv: cannot set %s: %m", env); + if (setenv(HISMACADDR, env, 1) != 0) + syslog(LOG_INFO, "setenv: cannot set %s: %m", HISMACADDR); } /* And send our request data to the waiting node */ diff --git a/sys/sys/param.h b/sys/sys/param.h index d77fa0361700..585731795636 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -57,7 +57,7 @@ * is created, otherwise 1. */ #undef __FreeBSD_version -#define __FreeBSD_version 700049 /* Master, propagated to newvers */ +#define __FreeBSD_version 700050 /* Master, propagated to newvers */ #ifndef LOCORE #include diff --git a/tools/regression/environ/Makefile b/tools/regression/environ/Makefile new file mode 100644 index 000000000000..62720d28abd1 --- /dev/null +++ b/tools/regression/environ/Makefile @@ -0,0 +1,14 @@ +# +# $FreeBSD$ +# +PROGS= envctl retention timings + +all clean test: +.for target in ${.TARGET} +.for prog in ${PROGS} + @${MAKE} -f Makefile.${prog} ${target} +.endfor +.if make(clean) + rm -f *~ +.endif +.endfor diff --git a/tools/regression/environ/Makefile.envctl b/tools/regression/environ/Makefile.envctl new file mode 100644 index 000000000000..6298a20646dd --- /dev/null +++ b/tools/regression/environ/Makefile.envctl @@ -0,0 +1,16 @@ +# +# $FreeBSD$ +# +SRCS= envctl.c +PROG= envctl + +CFLAGS+=-Wall -I../../../include + +CLEANFILES= ${PROG}.core + +NO_MAN= yes + +.include + +test: ${PROG} + @sh envtest.t diff --git a/tools/regression/environ/Makefile.retention b/tools/regression/environ/Makefile.retention new file mode 100644 index 000000000000..9e0687f484fe --- /dev/null +++ b/tools/regression/environ/Makefile.retention @@ -0,0 +1,16 @@ +# +# $FreeBSD$ +# +SRCS= retention.c +PROG= retention + +CFLAGS+=-Wall -I../../../include + +CLEANFILES= *~ ${PROG}.core + +NO_MAN= yes + +.include + +test: ${PROG} + @./${PROG} diff --git a/tools/regression/environ/Makefile.timings b/tools/regression/environ/Makefile.timings new file mode 100644 index 000000000000..88c4f7c7280b --- /dev/null +++ b/tools/regression/environ/Makefile.timings @@ -0,0 +1,16 @@ +# +# $FreeBSD$ +# +SRCS= timings.c +PROG= timings + +CFLAGS+=-Wall -I../../../include + +CLEANFILES= ${PROG}.core + +NO_MAN= yes + +.include + +test: ${PROG} + @./timings diff --git a/tools/regression/environ/envctl.c b/tools/regression/environ/envctl.c new file mode 100644 index 000000000000..2681741da723 --- /dev/null +++ b/tools/regression/environ/envctl.c @@ -0,0 +1,154 @@ +/*- + * Copyright (c) 2007 Sean C. Farley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include + + +#include +__FBSDID("$FreeBSD$"); + + +extern char **environ; + + +static void +dump_environ(void) +{ + char **environPtr; + + for (environPtr = environ; *environPtr != NULL; *environPtr++) + printf("%s\n", *environPtr); + + return; +} + + +static void +usage(const char *program) +{ + fprintf(stderr, "Usage: %s [-DGUcht] [-gu name] [-p name=value] " + "[(-S|-s name) value overwrite]\n\n" + "Options:\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" + " -g name\t\t\tgetenv(name)\n" + " -h\t\t\t\tHelp\n" + " -p name=value\t\t\tputenv(name=value)\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", + basename(program)); + + return; +} + + +int +main(int argc, char **argv) +{ + char *cleanEnv[] = { NULL }; + char arg; + const char *eol = "\n"; + const char *value; + + if (argc == 1) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + + while ((arg = getopt(argc, argv, "DGS:Ucg:hp:s:tu:")) != -1) { + switch (arg) { + case 'D': + errno = 0; + dump_environ(); + break; + + case 'c': + environ = cleanEnv; + break; + + case 'G': + value = getenv(NULL); + printf("%s%s", value == NULL ? "" : value, eol); + break; + + case 'g': + value = getenv(optarg); + printf("%s%s", value == NULL ? "" : value, eol); + break; + + case 'p': + errno = 0; + printf("%d %d%s", putenv(optarg), errno, eol); + break; + + case 'S': + errno = 0; + printf("%d %d%s", setenv(NULL, optarg, + atoi(argv[optind])), errno, eol); + optind += 1; + break; + + case 's': + errno = 0; + printf("%d %d%s", setenv(optarg, argv[optind], + atoi(argv[optind + 1])), errno, eol); + optind += 2; + break; + + case 't': + eol = " "; + break; + + case 'U': + printf("%d %d%s", unsetenv(NULL), errno, eol); + break; + + case 'u': + printf("%d %d%s", unsetenv(optarg), errno, eol); + break; + + case 'h': + default: + usage(argv[0]); + exit(EXIT_FAILURE); + } + } + + // Output a closing newline in test mode. + if (eol[0] == ' ') + printf("\n"); + + return (EXIT_SUCCESS); +} diff --git a/tools/regression/environ/envtest.t b/tools/regression/environ/envtest.t new file mode 100644 index 000000000000..e3aca952e2da --- /dev/null +++ b/tools/regression/environ/envtest.t @@ -0,0 +1,182 @@ +#!/bin/sh +# +# Copyright (c) 2007 Sean C. Farley +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer, +# without modification, immediately at the beginning of the file. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# $FreeBSD$ + + +# Initialization. +testndx=0 + + +# Testing function. +run_test() +{ + lasttest="${@}" + result=`./envctl -t "${@}"` + + if [ ${?} -ne 0 ] + then + echo "Test program failed" >&2 + exit 1 + fi + + return +} + + +# Perform test on results. +check_result() +{ + testndx=$((testndx + 1)) + + echo "${result}" | sed 's/[ \t]*$//' | grep -q "^${@}$" + if [ ${?} -eq 0 ] + then + echo "ok ${testndx}" + else + echo "not ok ${testndx} - '${lasttest}'" + fi + + return +} + + +# +# Regression tests +# + +# Setup environment for tests. +readonly BAR="bar" +readonly NEWBAR="newbar" +export FOO=${BAR} + + +# Gets from environ. +run_test -g FOO +check_result "${FOO}" + +run_test -c -g FOO +check_result "" + +run_test -g FOOBAR +check_result "" + +run_test -c -g FOOBAR +check_result "" + +run_test -G +check_result "" + + +# Sets. +run_test -s FOO ${NEWBAR} 0 -g FOO +check_result "0 0 ${BAR}" + +run_test -s FOO ${NEWBAR} 1 -g FOO +check_result "0 0 ${NEWBAR}" + +run_test -c -s FOO ${NEWBAR} 0 -g FOO +check_result "0 0 ${NEWBAR}" + +run_test -c -s FOO ${NEWBAR} 1 -g FOO +check_result "0 0 ${NEWBAR}" + +run_test -s "FOO=" ${NEWBAR} 1 -g FOO +check_result "-1 22 ${BAR}" + +run_test -s "=FOO" ${NEWBAR} 1 +check_result "-1 22" + +run_test -s "=" ${NEWBAR} 1 +check_result "-1 22" + +run_test -s "" ${NEWBAR} 1 +check_result "-1 22" + +run_test -S ${NEWBAR} 1 +check_result "-1 22" + +run_test -s FOO ${NEWBAR} 1 -s FOO ${BAR} 1 -g FOO +check_result "0 0 0 0 ${BAR}" + +run_test -c -s FOO ${NEWBAR} 1 -s FOO ${BAR} 1 -g FOO +check_result "0 0 0 0 ${BAR}" + +run_test -s FOO ${NEWBAR} 1 -s FOO ${BAR} 1 -s FOO ${NEWBAR} 1 -g FOO +check_result "0 0 0 0 0 0 ${NEWBAR}" + +run_test -s FOO ${NEWBAR} 1 -s FOO ${BAR} 1 -s FOO ${NEWBAR} 1 -s FOO ${BAR} 1\ + -g FOO +check_result "0 0 0 0 0 0 0 0 ${BAR}" + + +# Unsets. +run_test -u FOO -g FOO +check_result "0 0" + +run_test -c -u FOO -g FOO +check_result "0 0" + +run_test -U +check_result "-1 22" + +run_test -u "" +check_result "-1 22" + +run_test -u "=${BAR}" +check_result "-1 22" + +run_test -c -s FOO ${NEWBAR} 1 -g FOO -u FOO -g FOO +check_result "0 0 ${NEWBAR} 0 0" + + +# Puts. +run_test -p FOO=${NEWBAR} -g FOO +check_result "0 0 ${NEWBAR}" + +run_test -c -p FOO=${NEWBAR} -g FOO +check_result "0 0 ${NEWBAR}" + +run_test -p FOO -g FOO +check_result "-1 22 ${BAR}" + +run_test -p FOO=${BAR} -p FOO=${NEWBAR} -g FOO +check_result "0 0 0 0 ${NEWBAR}" + +run_test -p FOO=${BAR} -s FOO ${NEWBAR} 1 -g FOO +check_result "0 0 0 0 ${NEWBAR}" + +run_test -s FOO ${NEWBAR} 1 -p FOO=${BAR} -g FOO +check_result "0 0 0 0 ${BAR}" + +run_test -p FOO=${BAR} -u FOO +check_result "0 0 0 0" + +run_test -p FOO=${BAR} -s FOO ${NEWBAR} 1 -u FOO +check_result "0 0 0 0 0 0" + +run_test -s FOO ${NEWBAR} 1 -p FOO=${BAR} -u FOO +check_result "0 0 0 0 0 0" diff --git a/tools/regression/environ/retention.c b/tools/regression/environ/retention.c new file mode 100644 index 000000000000..7f75cd100e15 --- /dev/null +++ b/tools/regression/environ/retention.c @@ -0,0 +1,109 @@ +/*- + * Copyright (c) 2007 Sean C. Farley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include + + +#include +__FBSDID("$FreeBSD$"); + + +extern char **environ; +const char *envName = "FOOBAR"; +const char *envValSmall = "Hi"; +const char *envValLarge = "Hi, again"; +const char *envValAny = "Any value"; + + +int +main(int argc, char **argv) +{ + const char *env1 = NULL; + const char *env2 = NULL; + const char *env3 = NULL; + const char *env4 = NULL; + const char *env5 = NULL; + int testNdx; + + /* Clean slate. */ + environ = NULL; + testNdx = 0; + + /* Initial value of variable. */ + if (getenv(envName) != NULL) + printf("not "); + printf("ok %d - getenv(\"%s\")\n", ++testNdx, envName); + + /* Set value of variable to smaller value and get value. */ + if ((setenv(envName, envValSmall, 1) != 0) || + ((env1 = getenv(envName)) == NULL) || + (strcmp(env1, envValSmall) != 0)) + printf("not "); + printf("ok %d - setenv(\"%s\", \"%s\", 1)\n", ++testNdx, envName, + envValSmall); + + /* Unset variable. */ + if ((unsetenv(envName) == -1) || ((env2 = getenv(envName)) != NULL)) + printf("not "); + printf("ok %d - unsetenv(\"%s\")\n", ++testNdx, envName); + + /* Set variable to bigger value and get value. */ + if ((setenv(envName, envValLarge, 1) != 0) || + ((env3 = getenv(envName)) == NULL) || + (strcmp(env3, envValLarge) != 0)) + printf("not "); + printf("ok %d - setenv(\"%s\", \"%s\", 1)\n", ++testNdx, envName, + envValLarge); + + /* Set variable to smaller value and get value. */ + if ((setenv(envName, envValSmall, 1) != 0) || + ((env4 = getenv(envName)) == NULL) || + (strcmp(env4, envValSmall) != 0)) + printf("not "); + printf("ok %d - setenv(\"%s\", \"%s\", 1)\n", ++testNdx, envName, + envValSmall); + + /* Set variable to any value without overwrite and get value. */ + if ((setenv(envName, envValAny, 0) != 0) || + ((env5 = getenv(envName)) == NULL) || + (strcmp(env5, envValAny) == 0)) + printf("not "); + printf("ok %d - setenv(\"%s\", \"%s\", 0)\n", ++testNdx, envName, + envValAny); + + /* + * Verify FreeBSD-ism about allowing a program to keep old pointers without + * risk of segfaulting. + */ + if ((strcmp(env1, envValSmall) != 0) || + (strcmp(env3, envValSmall) != 0) || + (strcmp(env4, envValSmall) != 0)) + printf("not "); + printf("ok %d - old variables point to valid memory\n", ++testNdx); + + exit(EXIT_SUCCESS); +} diff --git a/tools/regression/environ/timings.c b/tools/regression/environ/timings.c new file mode 100644 index 000000000000..1bf3c910a108 --- /dev/null +++ b/tools/regression/environ/timings.c @@ -0,0 +1,195 @@ +/*- + * Copyright (c) 2007 Sean C. Farley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include + + +#include +__FBSDID("$FreeBSD$"); + + +const char value1[] = "Large ------------------ value"; +const char value2[] = "Small -- value"; +char nameValuePair[] = "less=more"; +const char name[] = "PATH"; +const char name2[] = "SHELL"; +const int MaxIterations = 1000000; +const char Tabs[] = "\t\t\t"; + + +static int +report_time(const char *action, struct timeval *startTime, + struct timeval *endTime) +{ + int actionLen; + int numTabs; + + actionLen = strlen(action); + numTabs = 3 - actionLen / 8; + + return (printf("Time spent executing %s:%.*s%f\n", action, numTabs, Tabs, + (endTime->tv_sec - startTime->tv_sec) + + (double)(endTime->tv_usec - startTime->tv_usec) / 1000000)); +} + + +int +main(int argc, char **argv) +{ + int iterations; + struct timeval endTime; + struct timeval startTime; + + /* + * getenv() on the existing environment. + */ + gettimeofday(&startTime, NULL); + + /* Iterate over setting variable. */ + for (iterations = 0; iterations < MaxIterations; iterations++) + if (getenv(name) == NULL) + err(EXIT_FAILURE, "getenv(name)"); + + gettimeofday(&endTime, NULL); + + report_time("getenv(name)", &startTime, &endTime); + + + /* + * setenv() a variable with a large value. + */ + gettimeofday(&startTime, NULL); + + /* 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); + + report_time("setenv(name, value1, 1)", &startTime, &endTime); + + + /* + * getenv() the new variable on the new environment. + */ + gettimeofday(&startTime, NULL); + + /* Iterate over setting variable. */ + for (iterations = 0; iterations < MaxIterations; iterations++) + /* Set large value to variable. */ + if (getenv(name) == NULL) + err(EXIT_FAILURE, "getenv(name)"); + + gettimeofday(&endTime, NULL); + + report_time("getenv(name)", &startTime, &endTime); + + + /* + * getenv() a different variable on the new environment. + */ + gettimeofday(&startTime, NULL); + + /* Iterate over setting variable. */ + for (iterations = 0; iterations < MaxIterations; iterations++) + /* Set large value to variable. */ + if (getenv(name2) == NULL) + err(EXIT_FAILURE, "getenv(name2)"); + + gettimeofday(&endTime, NULL); + + report_time("getenv(name2)", &startTime, &endTime); + + + /* + * setenv() a variable with a small value. + */ + gettimeofday(&startTime, NULL); + + /* 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); + + report_time("setenv(name, value2, 1)", &startTime, &endTime); + + + /* + * getenv() a different variable on the new environment. + */ + gettimeofday(&startTime, NULL); + + /* Iterate over setting variable. */ + for (iterations = 0; iterations < MaxIterations; iterations++) + /* Set large value to variable. */ + if (getenv(name2) == NULL) + err(EXIT_FAILURE, "getenv(name)"); + + gettimeofday(&endTime, NULL); + + report_time("getenv(name)", &startTime, &endTime); + + + /* + * getenv() a different variable on the new environment. + */ + gettimeofday(&startTime, NULL); + + /* Iterate over setting variable. */ + for (iterations = 0; iterations < MaxIterations; iterations++) + /* Set large value to variable. */ + if (getenv(name2) == NULL) + err(EXIT_FAILURE, "getenv(name2)"); + + gettimeofday(&endTime, NULL); + + report_time("getenv(name2)", &startTime, &endTime); + + + /* + * putenv() a variable with a small value. + */ + gettimeofday(&startTime, NULL); + + /* Iterate over setting variable. */ + for (iterations = 0; iterations < MaxIterations; iterations++) + if (putenv(nameValuePair) == -1) + err(EXIT_FAILURE, "putenv(nameValuePair)"); + + gettimeofday(&endTime, NULL); + + report_time("putenv(nameValuePair)", &startTime, &endTime); + + + exit(EXIT_SUCCESS); +} diff --git a/usr.bin/du/du.c b/usr.bin/du/du.c index 00c1f5e728d0..73583e320b94 100644 --- a/usr.bin/du/du.c +++ b/usr.bin/du/du.c @@ -140,16 +140,16 @@ main(int argc, char *argv[]) cflag = 1; break; case 'h': - putenv("BLOCKSIZE=512"); + setenv("BLOCKSIZE", "512", 1); hflag = 1; break; case 'k': hflag = 0; - putenv("BLOCKSIZE=1024"); + setenv("BLOCKSIZE", "1024", 1); break; case 'm': hflag = 0; - putenv("BLOCKSIZE=1048576"); + setenv("BLOCKSIZE", "1048576", 1); break; case 'n': nodumpflag = 1; diff --git a/usr.bin/env/env.c b/usr.bin/env/env.c index 815976fe5f7a..b11a0b772e91 100644 --- a/usr.bin/env/env.c +++ b/usr.bin/env/env.c @@ -67,6 +67,7 @@ main(int argc, char **argv) char *altpath, **ep, *p, **parg; char *cleanenv[1]; int ch, want_clear; + int rtrn; altpath = NULL; want_clear = 0; @@ -105,7 +106,11 @@ main(int argc, char **argv) for (argv += optind; *argv && (p = strchr(*argv, '=')); ++argv) { if (env_verbosity) fprintf(stderr, "#env setenv:\t%s\n", *argv); - (void)setenv(*argv, ++p, 1); + *p = '\0'; + rtrn = setenv(*argv, p + 1, 1); + *p = '='; + if (rtrn == -1) + err(EXIT_FAILURE, "setenv %s", *argv); } if (*argv) { if (altpath) diff --git a/usr.bin/limits/limits.c b/usr.bin/limits/limits.c index e976f369e2ca..0383ac2a1199 100644 --- a/usr.bin/limits/limits.c +++ b/usr.bin/limits/limits.c @@ -244,6 +244,7 @@ main(int argc, char *argv[]) int rcswhich, shelltype; int i, num_limits = 0; int ch, doeval = 0, doall = 0; + int rtrn; login_cap_t * lc = NULL; enum { ANY=0, SOFT=1, HARD=2, BOTH=3, DISPLAYONLY=4 } type = ANY; enum { RCSUNKNOWN=0, RCSSET=1, RCSSEL=2 } todo = RCSUNKNOWN; @@ -399,8 +400,13 @@ main(int argc, char *argv[]) login_close(lc); /* set leading environment variables, like eval(1) */ - while (*argv && (p = strchr(*argv, '='))) - (void)setenv(*argv++, ++p, 1); + while (*argv && (p = strchr(*argv, '='))) { + *p = '\0'; + rtrn = setenv(*argv++, p + 1, 1); + *p = '='; + if (rtrn == -1) + err(EXIT_FAILURE, "setenv %s", *argv); + } /* Set limits */ for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) { diff --git a/usr.bin/login/login.c b/usr.bin/login/login.c index 7827ea1da7d2..549e015c160a 100644 --- a/usr.bin/login/login.c +++ b/usr.bin/login/login.c @@ -766,10 +766,11 @@ export(const char *s) "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH", "IFS", "PATH", NULL }; + char *p; const char **pp; size_t n; - if (strlen(s) > 1024 || strchr(s, '=') == NULL) + if (strlen(s) > 1024 || (p = strchr(s, '=')) == NULL) return (0); if (strncmp(s, "LD_", 3) == 0) return (0); @@ -778,7 +779,9 @@ export(const char *s) if (s[n] == '=' && strncmp(s, *pp, n) == 0) return (0); } - (void)putenv(s); + *p = '\0'; + (void)setenv(s, p + 1, 1); + *p = '='; return (1); } diff --git a/usr.bin/su/su.c b/usr.bin/su/su.c index 76f76b61e64d..8bc54720c2a6 100644 --- a/usr.bin/su/su.c +++ b/usr.bin/su/su.c @@ -564,10 +564,14 @@ static void export_pam_environment(void) { char **pp; + char *p; for (pp = environ_pam; *pp != NULL; pp++) { - if (ok_to_export(*pp)) - putenv(*pp); + if (ok_to_export(*pp)) { + p = strchr(*pp, '='); + *p = '\0'; + setenv(*pp, p + 1, 1); + } free(*pp); } } diff --git a/usr.sbin/pstat/pstat.c b/usr.sbin/pstat/pstat.c index 95f782c4e0cd..15163652921c 100644 --- a/usr.sbin/pstat/pstat.c +++ b/usr.sbin/pstat/pstat.c @@ -135,16 +135,16 @@ main(int argc, char *argv[]) fileflag = 1; break; case 'g': - putenv("BLOCKSIZE=1G"); + setenv("BLOCKSIZE", "1G", 1); break; case 'h': humanflag = 1; break; case 'k': - putenv("BLOCKSIZE=1K"); + setenv("BLOCKSIZE", "1K", 1); break; case 'm': - putenv("BLOCKSIZE=1M"); + setenv("BLOCKSIZE", "1M", 1); break; case 'M': memf = optarg; diff --git a/usr.sbin/sade/main.c b/usr.sbin/sade/main.c index 45ed1bbe4f9a..5496e136c1bb 100644 --- a/usr.sbin/sade/main.c +++ b/usr.sbin/sade/main.c @@ -56,7 +56,7 @@ main(int argc, char **argv) /* XXX */ char *p = getenv("TERM"); if (p && strcmp(p, "cons25") == 0) - putenv("TERM=cons25w"); + setenv("TERM", "cons25w", 1); } #endif diff --git a/usr.sbin/sade/variable.c b/usr.sbin/sade/variable.c index bbbc2d29d826..7809deb003fa 100644 --- a/usr.sbin/sade/variable.c +++ b/usr.sbin/sade/variable.c @@ -296,6 +296,7 @@ free_variables(void) void pvariable_set(char *var) { + char *p; char tmp[1024]; if (!var) @@ -307,7 +308,9 @@ pvariable_set(char *var) if (index(var, '=') == NULL) msgFatal("Invalid variable format: %s", var); strlcat(tmp, var, 1024); - putenv(tmp); + p = strchr(tmp, '='); + *p = '\0'; + setenv(tmp, p + 1, 1); } char * diff --git a/usr.sbin/sysinstall/main.c b/usr.sbin/sysinstall/main.c index 7af8d44b61d5..149a0dc4256e 100644 --- a/usr.sbin/sysinstall/main.c +++ b/usr.sbin/sysinstall/main.c @@ -74,7 +74,7 @@ main(int argc, char **argv) /* XXX */ char *p = getenv("TERM"); if (p && strcmp(p, "cons25") == 0) - putenv("TERM=cons25w"); + setenv("TERM", "cons25w", 1); } #endif diff --git a/usr.sbin/sysinstall/variable.c b/usr.sbin/sysinstall/variable.c index e86104fd100a..694b8be44fa2 100644 --- a/usr.sbin/sysinstall/variable.c +++ b/usr.sbin/sysinstall/variable.c @@ -301,6 +301,7 @@ free_variables(void) void pvariable_set(char *var) { + char *p; char tmp[1024]; if (!var) @@ -312,7 +313,9 @@ pvariable_set(char *var) if (index(var, '=') == NULL) msgFatal("Invalid variable format: %s", var); strlcat(tmp, var, 1024); - putenv(tmp); + p = strchr(tmp, '='); + *p = '\0'; + setenv(tmp, p + 1, 1); } char *