diff options
Diffstat (limited to 'lib/libc/stdlib')
| -rw-r--r-- | lib/libc/stdlib/Makefile.inc | 4 | ||||
| -rw-r--r-- | lib/libc/stdlib/_env.c | 330 | ||||
| -rw-r--r-- | lib/libc/stdlib/getenv.c | 164 | ||||
| -rw-r--r-- | lib/libc/stdlib/local.h | 18 | ||||
| -rw-r--r-- | lib/libc/stdlib/putenv.c | 41 | ||||
| -rw-r--r-- | lib/libc/stdlib/setenv.c | 68 | ||||
| -rw-r--r-- | lib/libc/stdlib/system.c | 17 | ||||
| -rw-r--r-- | lib/libc/stdlib/unsetenv.c | 54 |
8 files changed, 472 insertions, 224 deletions
diff --git a/lib/libc/stdlib/Makefile.inc b/lib/libc/stdlib/Makefile.inc index 2d8777f0bd4..1067d2c1667 100644 --- a/lib/libc/stdlib/Makefile.inc +++ b/lib/libc/stdlib/Makefile.inc @@ -1,10 +1,10 @@ -# $NetBSD: Makefile.inc,v 1.74 2010/05/03 05:01:53 jruoho Exp $ +# $NetBSD: Makefile.inc,v 1.75 2010/11/14 18:11:43 tron Exp $ # from: @(#)Makefile.inc 8.3 (Berkeley) 2/4/95 # stdlib sources .PATH: ${ARCHDIR}/stdlib ${.CURDIR}/stdlib -SRCS+= _rand48.c \ +SRCS+= _env.c _rand48.c \ a64l.c abort.c atexit.c atof.c atoi.c atol.c atoll.c \ bsearch.c drand48.c exit.c \ getenv.c getopt.c getopt_long.c getsubopt.c \ diff --git a/lib/libc/stdlib/_env.c b/lib/libc/stdlib/_env.c new file mode 100644 index 00000000000..67f4c78d670 --- /dev/null +++ b/lib/libc/stdlib/_env.c @@ -0,0 +1,330 @@ +/* $NetBSD: _env.c,v 1.1 2010/11/14 18:11:43 tron Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Matthias Scheler. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <sys/rbtree.h> + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> + +#include "env.h" +#include "reentrant.h" +#include "local.h" + +/* + * Red-Black tree node for tracking memory used by environment variables. + * The tree is sorted by the address of the nodes themselves. + */ +typedef struct { + rb_node_t rb_node; + size_t length; + char data[]; +} env_node_t; + +/* Compare functions for above tree. */ +static signed int env_tree_compare_nodes(void *, const void *, const void *); +static signed int env_tree_compare_key(void *, const void *, const void *); + +/* Operations for above tree. */ +static const rb_tree_ops_t env_tree_ops = { + .rbto_compare_nodes = env_tree_compare_nodes, + .rbto_compare_key = env_tree_compare_key, + .rbto_node_offset = offsetof(env_node_t, rb_node), + .rbto_context = NULL +}; + +/* The single instance of above tree. */ +static rb_tree_t env_tree; + +/* The allocated environment. */ +static char **allocated_environ; +static size_t allocated_environ_size; + +#define ENV_ARRAY_SIZE_MIN 16 + +/* The lock protecting accces to the environment. */ +#ifdef _REENTRANT +static rwlock_t env_lock = RWLOCK_INITIALIZER; +#endif + +/* Our initialization function. */ +void __libc_env_init(void); + +/*ARGSUSED*/ +static signed int +env_tree_compare_nodes(void *ctx, const void *node_a, const void *node_b) +{ + uintptr_t addr_a, addr_b; + + addr_a = (uintptr_t)node_a; + addr_b = (uintptr_t)node_b; + + if (addr_a < addr_b) + return -1; + + if (addr_a > addr_b) + return 1; + + return 0; +} + +static signed int +env_tree_compare_key(void *ctx, const void *node, const void *key) +{ + return env_tree_compare_nodes(ctx, node, + (const uint8_t *)key - offsetof(env_node_t, data)); +} + +/* + * Determine the of the name in an environment string. Return 0 if the + * name is not valid. + */ +size_t +__envvarnamelen(const char *str, bool withequal) +{ + size_t l_name; + + if (str == NULL) + return 0; + + l_name = strcspn(str, "="); + if (l_name == 0) + return 0; + + if (withequal) { + if (str[l_name] != '=') + return 0; + } else { + if (str[l_name] == '=') + return 0; + } + + return l_name; +} + +/* + * Free memory occupied by environment variable if possible. This function + * must be called with the environment write locked. + */ +void +__freeenvvar(char *envvar) +{ + env_node_t *node; + + _DIAGASSERT(envvar != NULL); + node = rb_tree_find_node(&env_tree, envvar); + if (node != NULL) { + rb_tree_remove_node(&env_tree, node); + free(node); + } +} + +/* + * Allocate memory for an environment variable. This function must be called + * with the environment write locked. + */ +char * +__allocenvvar(size_t length) +{ + env_node_t *node; + + node = malloc(sizeof(*node) + length); + if (node != NULL) { + node->length = length; + rb_tree_insert_node(&env_tree, node); + return node->data; + } else { + return NULL; + } +} + +/* + * Check whether an enviroment variable is writable. This function must be + * called with the environment write locked as the caller will probably + * overwrite the enviroment variable afterwards. + */ +bool +__canoverwriteenvvar(char *envvar, size_t length) +{ + env_node_t *node; + + _DIAGASSERT(envvar != NULL); + + node = rb_tree_find_node(&env_tree, envvar); + return (node != NULL && length <= node->length); +} + +/* + * Get a (new) slot in the environment. This function must be called with + * the environment write locked. + */ +ssize_t +__getenvslot(const char *name, size_t l_name, bool allocate) +{ + size_t new_size, num_entries, required_size; + char **new_environ; + + /* Search for an existing environment variable of the given name. */ + num_entries = 0; + while (environ[num_entries] != NULL) { + if (strncmp(environ[num_entries], name, l_name) == 0 && + environ[num_entries][l_name] == '=') { + /* We found a match. */ + return num_entries; + } + num_entries ++; + } + + /* No match found, return if we don't want to allocate a new slot. */ + if (!allocate) + return -1; + + /* Create a new slot in the environment. */ + required_size = num_entries + 1; + if (environ == allocated_environ && + required_size < allocated_environ_size) { + size_t offset; + + /* + * Scrub the environment if somebody erased its contents by + * e.g. setting environ[0] to NULL. + */ + offset = required_size; + while (offset < allocated_environ_size && + environ[offset] != NULL) { + __freeenvvar(environ[offset]); + environ[offset] = NULL; + offset++; + } + + /* Return a free slot. */ + return num_entries; + } + + /* Determine size of a new environment array. */ + new_size = ENV_ARRAY_SIZE_MIN; + while (new_size <= required_size) + new_size <<= 1; + + /* Allocate a new environment array. */ + if (environ == allocated_environ) { + new_environ = realloc(environ, + new_size * sizeof(*new_environ)); + if (new_environ == NULL) + return -1; + } else { + free(allocated_environ); + allocated_environ = NULL; + allocated_environ_size = 0; + + new_environ = malloc(new_size * sizeof(*new_environ)); + if (new_environ == NULL) + return -1; + (void)memcpy(new_environ, environ, + num_entries * sizeof(*new_environ)); + } + + /* Clear remaining entries. */ + (void)memset(&new_environ[num_entries], 0, + (new_size - num_entries) * sizeof(*new_environ)); + + /* Use the new environent array. */ + environ = allocated_environ = new_environ; + allocated_environ_size = new_size; + + /* Return a free slot. */ + return num_entries; +} + +/* Find a string in the environment. */ +char * +__findenv(const char *name, size_t l_name) +{ + ssize_t offset; + + offset = __getenvslot(name, l_name, false); + return (offset != -1) ? environ[offset] + l_name + 1 : NULL; +} + +#ifdef _REENTRANT + +/* Lock the environment for read. */ +bool +__readlockenv(void) +{ + int error; + + error = rwlock_rdlock(&env_lock); + if (error == 0) + return true; + + errno = error; + return false; +} + +/* Lock the environment for write. */ +bool +__writelockenv(void) +{ + int error; + + error = rwlock_wrlock(&env_lock); + if (error == 0) + return true; + + errno = error; + return false; +} + +/* Unlock the environment for write. */ +bool +__unlockenv(void) +{ + int error; + + error = rwlock_unlock(&env_lock); + if (error == 0) + return true; + + errno = error; + return false; +} + +#endif + +/* Initialize environment memory RB tree. */ +void +__libc_env_init(void) +{ + rb_tree_init(&env_tree, &env_tree_ops); +} diff --git a/lib/libc/stdlib/getenv.c b/lib/libc/stdlib/getenv.c index 39a1894aaa5..6f939d1545b 100644 --- a/lib/libc/stdlib/getenv.c +++ b/lib/libc/stdlib/getenv.c @@ -1,4 +1,4 @@ -/* $NetBSD: getenv.c,v 1.32 2010/11/10 02:40:08 enami Exp $ */ +/* $NetBSD: getenv.c,v 1.33 2010/11/14 18:11:43 tron Exp $ */ /* * Copyright (c) 1987, 1993 @@ -34,7 +34,7 @@ #if 0 static char sccsid[] = "@(#)getenv.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: getenv.c,v 1.32 2010/11/10 02:40:08 enami Exp $"); +__RCSID("$NetBSD: getenv.c,v 1.33 2010/11/14 18:11:43 tron Exp $"); #endif #endif /* LIBC_SCCS and not lint */ @@ -43,16 +43,11 @@ __RCSID("$NetBSD: getenv.c,v 1.32 2010/11/10 02:40:08 enami Exp $"); #include <errno.h> #include <stdlib.h> #include <string.h> + +#include "env.h" #include "reentrant.h" #include "local.h" -#ifdef _REENTRANT -rwlock_t __environ_lock = RWLOCK_INITIALIZER; -#endif -char **__environ_malloced; -static char **saveenv; -static size_t environ_malloced_len; - __weak_alias(getenv_r, _getenv_r) /* @@ -65,139 +60,52 @@ __weak_alias(getenv_r, _getenv_r) char * getenv(const char *name) { - int offset; + size_t l_name; char *result; _DIAGASSERT(name != NULL); - rwlock_rdlock(&__environ_lock); - result = __findenv(name, &offset); - rwlock_unlock(&__environ_lock); + l_name = __envvarnamelen(name, false); + if (l_name == 0) + return NULL; + + result = NULL; + if (__readlockenv()) { + result = __findenv(name, l_name); + (void)__unlockenv(); + } + return result; } int getenv_r(const char *name, char *buf, size_t len) { - int offset; - char *result; - int rv = -1; + size_t l_name; + int rv; _DIAGASSERT(name != NULL); - rwlock_rdlock(&__environ_lock); - result = __findenv(name, &offset); - if (result == NULL) { - errno = ENOENT; - goto out; - } - if (strlcpy(buf, result, len) >= len) { - errno = ERANGE; - goto out; - } - rv = 0; -out: - rwlock_unlock(&__environ_lock); - return rv; -} - -int -__allocenv(int offset) -{ - char **p; - size_t required_len, new_len; - - if (offset == -1 || saveenv != environ) { - char **ptr; - for (ptr = environ, offset = 0; *ptr != NULL; ptr++) - offset++; - } - - /* one for potentially new entry one for NULL */ - required_len = offset + 2; - - if (required_len <= environ_malloced_len && saveenv == environ) - return 0; - - /* Double the size of the arrays until we meet the requirement. */ - new_len = environ_malloced_len ? environ_malloced_len : 16; - while (new_len < required_len) - new_len <<= 1; - - if (saveenv == environ) { /* just increase size */ - if ((p = realloc(saveenv, new_len * sizeof(*p))) == NULL) - return -1; - (void)memset(&p[environ_malloced_len], 0, - (new_len - environ_malloced_len) * sizeof(*p)); - saveenv = p; - } else { /* get new space */ - free(saveenv); - if ((saveenv = malloc(new_len * sizeof(*saveenv))) == NULL) - return -1; - (void)memcpy(saveenv, environ, - (required_len - 2) * sizeof(*saveenv)); - (void)memset(&saveenv[required_len - 2], 0, - (new_len - (required_len - 2)) * sizeof(*saveenv)); - } - environ = saveenv; - - p = realloc(__environ_malloced, new_len * sizeof(*p)); - if (p == NULL) + l_name = __envvarnamelen(name, false); + if (l_name == 0) return -1; - (void)memset(&p[environ_malloced_len], 0, - (new_len - environ_malloced_len) * sizeof(*p)); - environ_malloced_len = new_len; - __environ_malloced = p; - - return 0; -} - -/* - * Handle the case where a program tried to cleanup the environment - * by setting *environ = NULL; we attempt to cleanup all the malloced - * environ entries and we make sure that the entry following the new - * entry is NULL. - */ -void -__scrubenv(int offset) -{ - if (environ[++offset] == NULL) - return; - - for (; environ[offset]; offset++) { - if (environ[offset] == __environ_malloced[offset]) - free(__environ_malloced[offset]); - environ[offset] = __environ_malloced[offset] = NULL; - } -} - -/* - * __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. - * - * This routine *should* be a static; don't use it. - */ -char * -__findenv(const char *name, int *offset) -{ - size_t len; - const char *np; - char **p, *c; - - if (name == NULL || environ == NULL) - return NULL; - for (np = name; *np && *np != '='; ++np) - continue; - len = np - name; - for (p = environ; (c = *p) != NULL; ++p) - if (strncmp(c, name, len) == 0 && c[len] == '=') { - *offset = (int)(p - environ); - return c + len + 1; + rv = -1; + if (__readlockenv()) { + const char *value; + + value = __findenv(name, l_name); + if (value != NULL) { + if (strlcpy(buf, value, len) < len) { + rv = 0; + } else { + errno = ERANGE; + } + } else { + errno = ENOENT; } - *offset = (int)(p - environ); - return NULL; + (void)__unlockenv(); + } + + return rv; } diff --git a/lib/libc/stdlib/local.h b/lib/libc/stdlib/local.h index c6916225f7e..285a9b44861 100644 --- a/lib/libc/stdlib/local.h +++ b/lib/libc/stdlib/local.h @@ -1,4 +1,4 @@ -/* $NetBSD: local.h,v 1.5 2010/11/03 15:01:07 christos Exp $ */ +/* $NetBSD: local.h,v 1.6 2010/11/14 18:11:43 tron Exp $ */ /* * Copyright (c) 1997 Christos Zoulas. All rights reserved. @@ -24,13 +24,13 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -char *__findenv(const char *, int *); -int __allocenv(int); -void __scrubenv(int); +/* Environment handling. */ -#ifdef _REENTRANT -extern rwlock_t __environ_lock; -#endif +#include <sys/types.h> +#include <stdbool.h> -extern char **environ; -extern char **__environ_malloced; +extern size_t __envvarnamelen(const char *str, bool withequal); + +extern void __freeenvvar(char *envvar); +extern char *__allocenvvar(size_t length); +extern bool __canoverwriteenvvar(char *envvar, size_t length); diff --git a/lib/libc/stdlib/putenv.c b/lib/libc/stdlib/putenv.c index 1fac6dbbf0a..1f617b4f7b9 100644 --- a/lib/libc/stdlib/putenv.c +++ b/lib/libc/stdlib/putenv.c @@ -1,4 +1,4 @@ -/* $NetBSD: putenv.c,v 1.18 2010/11/03 15:01:07 christos Exp $ */ +/* $NetBSD: putenv.c,v 1.19 2010/11/14 18:11:43 tron Exp $ */ /*- * Copyright (c) 1988, 1993 @@ -34,7 +34,7 @@ #if 0 static char sccsid[] = "@(#)putenv.c 8.2 (Berkeley) 3/27/94"; #else -__RCSID("$NetBSD: putenv.c,v 1.18 2010/11/03 15:01:07 christos Exp $"); +__RCSID("$NetBSD: putenv.c,v 1.19 2010/11/14 18:11:43 tron Exp $"); #endif #endif /* LIBC_SCCS and not lint */ @@ -44,6 +44,8 @@ __RCSID("$NetBSD: putenv.c,v 1.18 2010/11/03 15:01:07 christos Exp $"); #include <errno.h> #include <stdlib.h> #include <string.h> + +#include "env.h" #include "reentrant.h" #include "local.h" @@ -54,35 +56,32 @@ __weak_alias(putenv,_putenv) int putenv(char *str) { - char *p; - int offset; + size_t l_name; + int rv; _DIAGASSERT(str != NULL); - if (str == NULL || strchr(str, '=') == NULL || *str == '=') { + l_name = __envvarnamelen(str, true); + if (l_name == 0) { errno = EINVAL; return -1; } - if (rwlock_wrlock(&__environ_lock) != 0) - return -1; + rv = -1; + if (__writelockenv()) { + ssize_t offset; - p = __findenv(str, &offset); + offset = __getenvslot(str, l_name, true); + if (offset != -1) { + if (environ[offset] != NULL) + __freeenvvar(environ[offset]); + environ[offset] = str; - if (__allocenv(offset) == -1) - goto bad; + rv = 0; + } - if (p != NULL && environ[offset] == __environ_malloced[offset]) { - free(__environ_malloced[offset]); - __environ_malloced[offset] = NULL; + (void)__unlockenv(); } - environ[offset] = str; - if (p == NULL) - __scrubenv(offset); - rwlock_unlock(&__environ_lock); - return 0; -bad: - rwlock_unlock(&__environ_lock); - return -1; + return rv; } diff --git a/lib/libc/stdlib/setenv.c b/lib/libc/stdlib/setenv.c index 0f8a85aa74a..0098b7b5c68 100644 --- a/lib/libc/stdlib/setenv.c +++ b/lib/libc/stdlib/setenv.c @@ -1,4 +1,4 @@ -/* $NetBSD: setenv.c,v 1.42 2010/11/03 15:01:07 christos Exp $ */ +/* $NetBSD: setenv.c,v 1.43 2010/11/14 18:11:43 tron Exp $ */ /* * Copyright (c) 1987, 1993 @@ -34,7 +34,7 @@ #if 0 static char sccsid[] = "@(#)setenv.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: setenv.c,v 1.42 2010/11/03 15:01:07 christos Exp $"); +__RCSID("$NetBSD: setenv.c,v 1.43 2010/11/14 18:11:43 tron Exp $"); #endif #endif /* LIBC_SCCS and not lint */ @@ -44,6 +44,8 @@ __RCSID("$NetBSD: setenv.c,v 1.42 2010/11/03 15:01:07 christos Exp $"); #include <errno.h> #include <stdlib.h> #include <string.h> + +#include "env.h" #include "reentrant.h" #include "local.h" @@ -59,36 +61,33 @@ __weak_alias(setenv,_setenv) int setenv(const char *name, const char *value, int rewrite) { - char *c, *f; - size_t l_value, size; - int offset; + size_t l_name, l_value, length; + ssize_t offset; + char *envvar; _DIAGASSERT(name != NULL); _DIAGASSERT(value != NULL); - if (name == NULL || value == NULL) { - errno = EINVAL; - return -1; - } - - size = strcspn(name, "="); - if (size == 0 || name[size] != '\0') { + l_name = __envvarnamelen(name, false); + if (l_name == 0 || value == NULL) { errno = EINVAL; return -1; } - if (rwlock_wrlock(&__environ_lock) != 0) + if (!__writelockenv()) return -1; - /* find if already exists */ - f = __findenv(name, &offset); - - if (__allocenv(offset) == -1) + /* Find slot in the enviroment. */ + offset = __getenvslot(name, l_name, true); + if (offset == -1) goto bad; l_value = strlen(value); + length = l_name + l_value + 2; - if (f != NULL) { + /* Handle overwriting a current environt variable. */ + envvar = environ[offset]; + if (envvar != NULL) { if (!rewrite) goto good; /* @@ -96,35 +95,34 @@ setenv(const char *name, const char *value, int rewrite) * whether there is enough space. If so simply overwrite the * existing value. */ - if (environ[offset] == __environ_malloced[offset] && - strlen(f) >= l_value) { - c = f; + if (__canoverwriteenvvar(envvar, length)) { + envvar += l_name + 1; goto copy; } } - /* name + `=' + value */ - if ((c = malloc(size + l_value + 2)) == NULL) + + /* Allocate memory for name + `=' + value + NUL. */ + if ((envvar = __allocenvvar(length)) == NULL) goto bad; - if (environ[offset] == __environ_malloced[offset]) - free(__environ_malloced[offset]); + if (environ[offset] != NULL) + __freeenvvar(environ[offset]); - environ[offset] = c; - __environ_malloced[offset] = c; + environ[offset] = envvar; - if (f == NULL) - __scrubenv(offset); + (void)memcpy(envvar, name, l_name); - (void)memcpy(c, name, size); - c += size; - *c++ = '='; + envvar += l_name; + *envvar++ = '='; copy: - (void)memcpy(c, value, l_value + 1); + (void)memcpy(envvar, value, l_value + 1); + good: - rwlock_unlock(&__environ_lock); + (void)__unlockenv(); return 0; + bad: - rwlock_unlock(&__environ_lock); + (void)__unlockenv(); return -1; } diff --git a/lib/libc/stdlib/system.c b/lib/libc/stdlib/system.c index dd02987a5aa..f0fc1207ee8 100644 --- a/lib/libc/stdlib/system.c +++ b/lib/libc/stdlib/system.c @@ -1,4 +1,4 @@ -/* $NetBSD: system.c,v 1.22 2008/08/27 06:45:02 christos Exp $ */ +/* $NetBSD: system.c,v 1.23 2010/11/14 18:11:43 tron Exp $ */ /* * Copyright (c) 1988, 1993 @@ -34,7 +34,7 @@ #if 0 static char sccsid[] = "@(#)system.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: system.c,v 1.22 2008/08/27 06:45:02 christos Exp $"); +__RCSID("$NetBSD: system.c,v 1.23 2010/11/14 18:11:43 tron Exp $"); #endif #endif /* LIBC_SCCS and not lint */ @@ -46,12 +46,9 @@ __RCSID("$NetBSD: system.c,v 1.22 2008/08/27 06:45:02 christos Exp $"); #include <stdlib.h> #include <unistd.h> #include <paths.h> -#include "reentrant.h" -#ifdef _REENTRANT -extern rwlock_t __environ_lock; -#endif -extern char **environ; +#include "env.h" +#include "reentrant.h" int system(command) @@ -93,10 +90,10 @@ system(command) return -1; } - rwlock_rdlock(&__environ_lock); + (void)__readlockenv(); switch(pid = vfork()) { case -1: /* error */ - rwlock_unlock(&__environ_lock); + (void)__unlockenv(); sigaction(SIGINT, &intsa, NULL); sigaction(SIGQUIT, &quitsa, NULL); (void)sigprocmask(SIG_SETMASK, &omask, NULL); @@ -108,7 +105,7 @@ system(command) execve(_PATH_BSHELL, __UNCONST(argp), environ); _exit(127); } - rwlock_unlock(&__environ_lock); + (void)__unlockenv(); while (waitpid(pid, &pstat, 0) == -1) { if (errno != EINTR) { diff --git a/lib/libc/stdlib/unsetenv.c b/lib/libc/stdlib/unsetenv.c index c77724aa2c7..5796f3861c5 100644 --- a/lib/libc/stdlib/unsetenv.c +++ b/lib/libc/stdlib/unsetenv.c @@ -1,4 +1,4 @@ -/* $NetBSD: unsetenv.c,v 1.9 2010/09/30 12:41:33 tron Exp $ */ +/* $NetBSD: unsetenv.c,v 1.10 2010/11/14 18:11:43 tron Exp $ */ /* * Copyright (c) 1987, 1993 @@ -34,7 +34,7 @@ #if 0 static char sccsid[] = "from: @(#)setenv.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: unsetenv.c,v 1.9 2010/09/30 12:41:33 tron Exp $"); +__RCSID("$NetBSD: unsetenv.c,v 1.10 2010/11/14 18:11:43 tron Exp $"); #endif #endif /* LIBC_SCCS and not lint */ @@ -45,6 +45,8 @@ __RCSID("$NetBSD: unsetenv.c,v 1.9 2010/09/30 12:41:33 tron Exp $"); #include <stdlib.h> #include <string.h> #include <bitstring.h> + +#include "env.h" #include "reentrant.h" #include "local.h" @@ -55,36 +57,50 @@ __RCSID("$NetBSD: unsetenv.c,v 1.9 2010/09/30 12:41:33 tron Exp $"); int unsetenv(const char *name) { - int offset; + size_t l_name; + ssize_t r_offset, w_offset; _DIAGASSERT(name != NULL); - if (name == NULL || *name == '\0' || strchr(name, '=') != NULL) { + l_name = __envvarnamelen(name, false); + if (l_name == 0) { errno = EINVAL; return -1; } - if (rwlock_wrlock(&__environ_lock) != 0) + if (!__writelockenv()) return -1; - if (__allocenv(-1) == -1) { - rwlock_unlock(&__environ_lock); - return -1; + /* Search for the given name in the environment. */ + r_offset = __getenvslot(name, l_name, false); + if (r_offset == -1) { + /* Not found. */ + (void)__unlockenv(); + return 0; } + __freeenvvar(environ[r_offset]); - while (__findenv(name, &offset) != NULL) { /* if set multiple times */ - if (environ[offset] == __environ_malloced[offset]) - free(__environ_malloced[offset]); - - while (environ[offset] != NULL) { - environ[offset] = environ[offset + 1]; - __environ_malloced[offset] = - __environ_malloced[offset + 1]; - offset++; + /* + * Remove all matches from the environment and free the associated + * memory if possible. + */ + w_offset = r_offset; + while (environ[++r_offset] != NULL) { + if (strncmp(environ[r_offset], name, l_name) != 0 || + environ[r_offset][l_name] != '=') { + /* Not a match, keep this entry. */ + environ[w_offset++] = environ[r_offset]; + } else { + /* We found another match. */ + __freeenvvar(environ[r_offset]); } - __environ_malloced[offset] = NULL; } - rwlock_unlock(&__environ_lock); + /* Clear out remaining stale entries in the environment vector. */ + do { + environ[w_offset++] = NULL; + } while (w_offset < r_offset); + + (void)__unlockenv(); return 0; } |
