summaryrefslogtreecommitdiff
path: root/lib/libc/stdlib
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libc/stdlib')
-rw-r--r--lib/libc/stdlib/Makefile.inc4
-rw-r--r--lib/libc/stdlib/_env.c330
-rw-r--r--lib/libc/stdlib/getenv.c164
-rw-r--r--lib/libc/stdlib/local.h18
-rw-r--r--lib/libc/stdlib/putenv.c41
-rw-r--r--lib/libc/stdlib/setenv.c68
-rw-r--r--lib/libc/stdlib/system.c17
-rw-r--r--lib/libc/stdlib/unsetenv.c54
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;
}