From 0d0145515d3e4debbdcce2d3135bd0fd94ae0da8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Thu, 10 Mar 2005 22:41:49 +0000 Subject: [PATCH] Implemented userland env handling; it longer uses syscalls for this - I even made it thread-safe, although this is not required by POSIX. Implemented unsetenv(), improved putenv(). git-svn-id: file:///srv/svn/repos/haiku/trunk/current@11668 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/kernel/libroot/posix/stdlib/env.c | 214 +++++++++++++++++++++++--- 1 file changed, 191 insertions(+), 23 deletions(-) diff --git a/src/kernel/libroot/posix/stdlib/env.c b/src/kernel/libroot/posix/stdlib/env.c index 997e250ef0..e322b12122 100644 --- a/src/kernel/libroot/posix/stdlib/env.c +++ b/src/kernel/libroot/posix/stdlib/env.c @@ -1,14 +1,15 @@ /* -** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. -** Distributed under the terms of the Haiku License. -** -** Copyright 2001, Travis Geiselbrecht. All rights reserved. -** Distributed under the terms of the NewOS License. -*/ + * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ -#include +#include +#include +#include + +#include #include #include #include @@ -22,45 +23,212 @@ return err; +static sem_id sEnvLock; +static bool sCopied; + char **environ = NULL; -int -setenv(const char *name, const char *value, int overwrite) +static int32 +count_variables(void) { - status_t status = sys_setenv(name, value, overwrite); + int32 i = 0; + while (environ[i]) + i++; - RETURN_AND_SET_ERRNO(status); + return i; } +static int32 +add_variable(void) +{ + int32 count = count_variables() + 1; + char **newEnv = malloc((count + 1) * sizeof(char *)); + if (newEnv == NULL) + return B_NO_MEMORY; + + memcpy(newEnv, environ, count * sizeof(char *)); + newEnv[count] = NULL; + // null terminate the array + + return count - 1; +} + + +static char * +find_variable(const char *name, int32 length, int32 *_index) +{ + int32 i; + + for (i = 0; environ[i] != NULL; i++) { + if (!strncmp(name, environ[i], length) && environ[i][length] == '=') { + if (_index != NULL) + *_index = i; + return environ[i]; + } + } + + return NULL; +} + + +static status_t +copy_environ_to_heap_if_needed(void) +{ + char **newEnv; + int32 i; + + if (sCopied) + return B_OK; + + newEnv = malloc((count_variables() + 1) * sizeof(char *)); + if (newEnv == NULL) + return B_NO_MEMORY; + + for (i = 0; environ[i]; i++) { + newEnv[i] = strdup(environ[i]); + } + + newEnv[i] = NULL; + // null terminate the array + + sCopied = true; + return B_OK; +} + + +static status_t +update_variable(const char *name, int32 length, const char *value, bool overwrite) +{ + status_t status = B_OK; + bool update = false; + int32 index; + char *env; + + acquire_sem(sEnvLock); + copy_environ_to_heap_if_needed(); + + env = find_variable(name, length, &index); + if (env != NULL && overwrite) { + // change variable + free(environ[index]); + update = true; + } else if (env == NULL) { + // add variable + index = add_variable(); + update = true; + } + + if (update) { + environ[index] = malloc(length + 2 + strlen(value)); + if (environ[index] != NULL) { + memcpy(environ[index], name, length); + environ[index][length] = '='; + strcpy(environ[index] + length + 1, value); + } else + status = B_NO_MEMORY; + } + + release_sem(sEnvLock); + return status; +} + + +static void +environ_fork_hook(void) +{ + sEnvLock = create_sem(1, "env lock"); +} + + +void +__init_env(const struct uspace_program_args *args) +{ + // Following POSIX, there is no need to make any of the + // environment functions thread-safe - but we do it anyway + sEnvLock = create_sem(1, "env lock"); + environ = args->envp; + + atfork(environ_fork_hook); +} + + +// #pragma mark - + + char * getenv(const char *name) { + int32 length = strlen(name); char *value; - int rc; - - rc = sys_getenv(name, &value); - if (rc < 0) + + acquire_sem(sEnvLock); + value = find_variable(name, length, NULL); + release_sem(sEnvLock); + + if (value == NULL) return NULL; - return value; + + return value + length + 1; +} + + +int +setenv(const char *name, const char *value, int overwrite) +{ + status_t status; + + if (name == NULL || name[0] == '\0' || strchr(name, '=') != NULL) { + errno = B_BAD_VALUE; + return -1; + } + + status = update_variable(name, strlen(name), value, overwrite); + RETURN_AND_SET_ERRNO(status); +} + + +int +unsetenv(const char *name) +{ + int32 index, length; + char *env; + + if (name == NULL || name[0] == '\0' || strchr(name, '=') != NULL) { + errno = B_BAD_VALUE; + return -1; + } + + length = strlen(name); + + acquire_sem(sEnvLock); + copy_environ_to_heap_if_needed(); + + env = find_variable(name, length, &index); + if (env != NULL) { + // we don't free the memory for the slot, we just move the array contents + free(env); + memmove(environ + index, environ + index + 1, sizeof(char *) * (count_variables() + 1)); + } + + release_sem(sEnvLock); + return 0; } int putenv(const char *string) { - char name[64]; char *value = strchr(string, '='); + status_t status; - if (value == NULL || value - string >= (int)sizeof(name)) { - errno = EINVAL; + if (value == NULL) { + errno = B_BAD_VALUE; return -1; } - strlcpy(name, string, value + 1 - string); - value++; - - return setenv(name, value, true); + status = update_variable(string, value - string, value + 1, true); + RETURN_AND_SET_ERRNO(status); }