Module Name: src
Committed By: tron
Date: Sun Nov 14 18:11:43 UTC 2010
Modified Files:
src/lib/libc/compat/stdlib: compat_unsetenv.c
src/lib/libc/gen: popen.c
src/lib/libc/misc: initfini.c
src/lib/libc/stdlib: Makefile.inc getenv.c local.h putenv.c setenv.c
system.c unsetenv.c
Added Files:
src/lib/libc/include: env.h
src/lib/libc/stdlib: _env.c
Log Message:
Improve and simplify implementation of *env(3) functions:
- Use RB tree to keep track of memory allocated via setenv(3) as
suggested by Enami Tsugutomo in private e-mail.
This simplifies the code a lot as we no longer need to keep the size
of "environ" in sync with an array of allocated environment variables.
It also makes it possible to free environment variables in unsetenv(3)
if something has changed the order of the "environ" array.
- Fix a bug in getenv(3) and getenv_r(3) which would return bogus
results e.g. for " getenv("A=B") " if an environment variable "A"
with value "B=C" exists.
- Clean up the internal functions:
- Don't expose the read/write lock for the environment to other parts
of "libc". Provide locking functions instead.
- Use "bool" to report success or failure.
- Use "ssize_t" or "size_t" instead of "int" for indexes.
- Provide internal functions with simpler interfaces e.g. don't
combine return values and reference arguments.
- Don't copy "environ" into an allocated block unless we really need
to grow it.
Code reviewed by Joerg Sonnenberger and Christos Zoulas, tested by
Joerg Sonnenberger and me. These changes also fix problems in
zsh 4.3.* and pam_ssh according to Joerg.
To generate a diff of this commit:
cvs rdiff -u -r1.2 -r1.3 src/lib/libc/compat/stdlib/compat_unsetenv.c
cvs rdiff -u -r1.29 -r1.30 src/lib/libc/gen/popen.c
cvs rdiff -u -r0 -r1.1 src/lib/libc/include/env.h
cvs rdiff -u -r1.6 -r1.7 src/lib/libc/misc/initfini.c
cvs rdiff -u -r1.74 -r1.75 src/lib/libc/stdlib/Makefile.inc
cvs rdiff -u -r0 -r1.1 src/lib/libc/stdlib/_env.c
cvs rdiff -u -r1.32 -r1.33 src/lib/libc/stdlib/getenv.c
cvs rdiff -u -r1.5 -r1.6 src/lib/libc/stdlib/local.h
cvs rdiff -u -r1.18 -r1.19 src/lib/libc/stdlib/putenv.c
cvs rdiff -u -r1.42 -r1.43 src/lib/libc/stdlib/setenv.c
cvs rdiff -u -r1.22 -r1.23 src/lib/libc/stdlib/system.c
cvs rdiff -u -r1.9 -r1.10 src/lib/libc/stdlib/unsetenv.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/lib/libc/compat/stdlib/compat_unsetenv.c
diff -u src/lib/libc/compat/stdlib/compat_unsetenv.c:1.2 src/lib/libc/compat/stdlib/compat_unsetenv.c:1.3
--- src/lib/libc/compat/stdlib/compat_unsetenv.c:1.2 Thu Sep 23 17:30:49 2010
+++ src/lib/libc/compat/stdlib/compat_unsetenv.c Sun Nov 14 18:11:42 2010
@@ -1,4 +1,4 @@
-/* $NetBSD: compat_unsetenv.c,v 1.2 2010/09/23 17:30:49 christos Exp $ */
+/* $NetBSD: compat_unsetenv.c,v 1.3 2010/11/14 18:11:42 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: compat_unsetenv.c,v 1.2 2010/09/23 17:30:49 christos Exp $");
+__RCSID("$NetBSD: compat_unsetenv.c,v 1.3 2010/11/14 18:11:42 tron Exp $");
#endif
#endif /* LIBC_SCCS and not lint */
@@ -54,16 +54,12 @@
__weak_alias(unsetenv,_unsetenv)
#endif
-#ifdef _REENTRANT
-extern rwlock_t __environ_lock;
-#endif
+#include "env.h"
__warn_references(unsetenv,
"warning: reference to compatibility unsetenv();"
" include <stdlib.h> for correct reference")
-extern char **environ;
-
/*
* unsetenv(name) --
* Delete environmental variable "name".
@@ -71,15 +67,21 @@
void
unsetenv(const char *name)
{
- char **p;
- int offset;
+ size_t l_name;
_DIAGASSERT(name != NULL);
- rwlock_wrlock(&__environ_lock);
- while (__findenv(name, &offset)) /* if set multiple times */
- for (p = &environ[offset];; ++p)
- if (!(*p = *(p + 1)))
- break;
- rwlock_unlock(&__environ_lock);
+ l_name = strlen(name);
+ if (__writelockenv()) {
+ ssize_t offset;
+
+ while ((offset = __getenvslot(name, l_name, false)) != -1) {
+ char **p;
+ for (p = &environ[offset]; ; ++p) {
+ if ((*p = *(p + 1)) == NULL)
+ break;
+ }
+ }
+ (void)__unlockenv();
+ }
}
Index: src/lib/libc/gen/popen.c
diff -u src/lib/libc/gen/popen.c:1.29 src/lib/libc/gen/popen.c:1.30
--- src/lib/libc/gen/popen.c:1.29 Sun Oct 15 16:12:02 2006
+++ src/lib/libc/gen/popen.c Sun Nov 14 18:11:42 2010
@@ -1,4 +1,4 @@
-/* $NetBSD: popen.c,v 1.29 2006/10/15 16:12:02 christos Exp $ */
+/* $NetBSD: popen.c,v 1.30 2010/11/14 18:11:42 tron Exp $ */
/*
* Copyright (c) 1988, 1993
@@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 5/3/95";
#else
-__RCSID("$NetBSD: popen.c,v 1.29 2006/10/15 16:12:02 christos Exp $");
+__RCSID("$NetBSD: popen.c,v 1.30 2010/11/14 18:11:42 tron Exp $");
#endif
#endif /* LIBC_SCCS and not lint */
@@ -54,6 +54,8 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+
+#include "env.h"
#include "reentrant.h"
#ifdef __weak_alias
@@ -61,10 +63,6 @@
__weak_alias(pclose,_pclose)
#endif
-#ifdef _REENTRANT
-extern rwlock_t __environ_lock;
-#endif
-
static struct pid {
struct pid *next;
FILE *fp;
@@ -111,13 +109,13 @@
return (NULL);
}
- rwlock_rdlock(&pidlist_lock);
- rwlock_rdlock(&__environ_lock);
+ (void)rwlock_rdlock(&pidlist_lock);
+ (void)__readlockenv();
switch (pid = vfork()) {
case -1: /* Error. */
serrno = errno;
- rwlock_unlock(&__environ_lock);
- rwlock_unlock(&pidlist_lock);
+ (void)__unlockenv();
+ (void)rwlock_unlock(&pidlist_lock);
free(cur);
(void)close(pdes[0]);
(void)close(pdes[1]);
@@ -155,7 +153,7 @@
_exit(127);
/* NOTREACHED */
}
- rwlock_unlock(&__environ_lock);
+ (void)__unlockenv();
/* Parent; assume fdopen can't fail. */
if (*xtype == 'r') {
@@ -177,7 +175,7 @@
cur->pid = pid;
cur->next = pidlist;
pidlist = cur;
- rwlock_unlock(&pidlist_lock);
+ (void)rwlock_unlock(&pidlist_lock);
return (iop);
}
@@ -204,7 +202,7 @@
if (cur->fp == iop)
break;
if (cur == NULL) {
- rwlock_unlock(&pidlist_lock);
+ (void)rwlock_unlock(&pidlist_lock);
return (-1);
}
@@ -216,7 +214,7 @@
else
last->next = cur->next;
- rwlock_unlock(&pidlist_lock);
+ (void)rwlock_unlock(&pidlist_lock);
do {
pid = waitpid(cur->pid, &pstat, 0);
Index: src/lib/libc/misc/initfini.c
diff -u src/lib/libc/misc/initfini.c:1.6 src/lib/libc/misc/initfini.c:1.7
--- src/lib/libc/misc/initfini.c:1.6 Mon Jun 28 21:58:02 2010
+++ src/lib/libc/misc/initfini.c Sun Nov 14 18:11:42 2010
@@ -1,4 +1,4 @@
-/* $NetBSD: initfini.c,v 1.6 2010/06/28 21:58:02 joerg Exp $ */
+/* $NetBSD: initfini.c,v 1.7 2010/11/14 18:11:42 tron Exp $ */
/*-
* Copyright (c) 2007 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
-__RCSID("$NetBSD: initfini.c,v 1.6 2010/06/28 21:58:02 joerg Exp $");
+__RCSID("$NetBSD: initfini.c,v 1.7 2010/11/14 18:11:42 tron Exp $");
#ifdef _LIBC
#include "namespace.h"
@@ -42,6 +42,7 @@
void __libc_thr_init(void);
void __libc_atomic_init(void);
void __libc_atexit_init(void);
+void __libc_env_init(void);
/* LINTED used */
void
@@ -59,4 +60,7 @@
/* Initialize the atexit mutexes */
__libc_atexit_init();
+
+ /* Initialize environment memory RB tree. */
+ __libc_env_init();
}
Index: src/lib/libc/stdlib/Makefile.inc
diff -u src/lib/libc/stdlib/Makefile.inc:1.74 src/lib/libc/stdlib/Makefile.inc:1.75
--- src/lib/libc/stdlib/Makefile.inc:1.74 Mon May 3 05:01:53 2010
+++ src/lib/libc/stdlib/Makefile.inc Sun Nov 14 18:11:43 2010
@@ -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 \
Index: src/lib/libc/stdlib/getenv.c
diff -u src/lib/libc/stdlib/getenv.c:1.32 src/lib/libc/stdlib/getenv.c:1.33
--- src/lib/libc/stdlib/getenv.c:1.32 Wed Nov 10 02:40:08 2010
+++ src/lib/libc/stdlib/getenv.c Sun Nov 14 18:11:43 2010
@@ -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 @@
#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 @@
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;
}
Index: src/lib/libc/stdlib/local.h
diff -u src/lib/libc/stdlib/local.h:1.5 src/lib/libc/stdlib/local.h:1.6
--- src/lib/libc/stdlib/local.h:1.5 Wed Nov 3 15:01:07 2010
+++ src/lib/libc/stdlib/local.h Sun Nov 14 18:11:43 2010
@@ -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);
Index: src/lib/libc/stdlib/putenv.c
diff -u src/lib/libc/stdlib/putenv.c:1.18 src/lib/libc/stdlib/putenv.c:1.19
--- src/lib/libc/stdlib/putenv.c:1.18 Wed Nov 3 15:01:07 2010
+++ src/lib/libc/stdlib/putenv.c Sun Nov 14 18:11:43 2010
@@ -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 @@
#include <errno.h>
#include <stdlib.h>
#include <string.h>
+
+#include "env.h"
#include "reentrant.h"
#include "local.h"
@@ -54,35 +56,32 @@
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;
-
- p = __findenv(str, &offset);
+ rv = -1;
+ if (__writelockenv()) {
+ ssize_t 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;
}
Index: src/lib/libc/stdlib/setenv.c
diff -u src/lib/libc/stdlib/setenv.c:1.42 src/lib/libc/stdlib/setenv.c:1.43
--- src/lib/libc/stdlib/setenv.c:1.42 Wed Nov 3 15:01:07 2010
+++ src/lib/libc/stdlib/setenv.c Sun Nov 14 18:11:43 2010
@@ -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 @@
#include <errno.h>
#include <stdlib.h>
#include <string.h>
+
+#include "env.h"
#include "reentrant.h"
#include "local.h"
@@ -59,36 +61,33 @@
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 @@
* 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;
}
Index: src/lib/libc/stdlib/system.c
diff -u src/lib/libc/stdlib/system.c:1.22 src/lib/libc/stdlib/system.c:1.23
--- src/lib/libc/stdlib/system.c:1.22 Wed Aug 27 06:45:02 2008
+++ src/lib/libc/stdlib/system.c Sun Nov 14 18:11:43 2010
@@ -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 @@
#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 @@
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 @@
execve(_PATH_BSHELL, __UNCONST(argp), environ);
_exit(127);
}
- rwlock_unlock(&__environ_lock);
+ (void)__unlockenv();
while (waitpid(pid, &pstat, 0) == -1) {
if (errno != EINTR) {
Index: src/lib/libc/stdlib/unsetenv.c
diff -u src/lib/libc/stdlib/unsetenv.c:1.9 src/lib/libc/stdlib/unsetenv.c:1.10
--- src/lib/libc/stdlib/unsetenv.c:1.9 Thu Sep 30 12:41:33 2010
+++ src/lib/libc/stdlib/unsetenv.c Sun Nov 14 18:11:43 2010
@@ -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 @@
#include <stdlib.h>
#include <string.h>
#include <bitstring.h>
+
+#include "env.h"
#include "reentrant.h"
#include "local.h"
@@ -55,36 +57,50 @@
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;
}
Added files:
Index: src/lib/libc/include/env.h
diff -u /dev/null src/lib/libc/include/env.h:1.1
--- /dev/null Sun Nov 14 18:11:43 2010
+++ src/lib/libc/include/env.h Sun Nov 14 18:11:42 2010
@@ -0,0 +1,65 @@
+/* $NetBSD: env.h,v 1.1 2010/11/14 18:11:42 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/types.h>
+
+#include <stdbool.h>
+
+#include "reentrant.h"
+
+extern ssize_t __getenvslot(const char *name, size_t l_name, bool allocate);
+extern char *__findenv(const char *name, size_t l_name);
+
+#ifdef _REENTRANT
+extern bool __readlockenv(void);
+extern bool __writelockenv(void);
+extern bool __unlockenv(void);
+#else
+static __inline bool
+__readlockenv(void)
+{
+ return true;
+}
+
+static __inline bool
+__writelockenv(void)
+{
+ return true;
+}
+
+static __inline bool
+__unlocklockenv(void)
+{
+ return true;
+}
+#endif
+
+extern char **environ;
Index: src/lib/libc/stdlib/_env.c
diff -u /dev/null src/lib/libc/stdlib/_env.c:1.1
--- /dev/null Sun Nov 14 18:11:43 2010
+++ src/lib/libc/stdlib/_env.c Sun Nov 14 18:11:43 2010
@@ -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);
+}