diff --git a/headers/posix/stdlib.h b/headers/posix/stdlib.h index 1aecc1eed0..5c1e81534f 100644 --- a/headers/posix/stdlib.h +++ b/headers/posix/stdlib.h @@ -22,21 +22,21 @@ /* random data structures (for thread-safe random numbers) */ struct random_data { - int *fptr; - int *rptr; - int *state; - int rand_type; - int rand_deg; - int rand_sep; - int *end_ptr; + int *fptr; + int *rptr; + int *state; + int rand_type; + int rand_deg; + int rand_sep; + int *end_ptr; }; struct drand48_data { - unsigned short int x[3]; - unsigned short int a[3]; - unsigned short int c; - unsigned short int old_x[3]; - int init; + unsigned short int x[3]; + unsigned short int a[3]; + unsigned short int c; + unsigned short int old_x[3]; + int init; }; @@ -73,6 +73,7 @@ extern char *gcvt(double value, int digits, char *buffer); /* environment variables */ extern char **environ; +extern int clearenv(void); extern char *getenv(const char *name); extern int putenv(const char *string); extern int setenv(char const *name, char const *value, int rewrite); @@ -102,7 +103,7 @@ extern int random_r(struct random_data *data, int *result); extern int srandom_r(unsigned int seed, struct random_data *data); extern char *initstate(unsigned int seed, char *state, size_t size); extern char *setstate(char *state); -extern int initstate_r(unsigned int seed, void *stateBuffer, +extern int initstate_r(unsigned int seed, void *stateBuffer, size_t stateLength, struct random_data *data); extern int setstate_r(void *stateBuffer, struct random_data *data); @@ -164,7 +165,7 @@ extern size_t mbstowcs(wchar_t *pwcs, const char *string, size_t maxSize); extern size_t wcstombs(char *string, const wchar_t *pwcs, size_t maxSize); /* crypt */ -extern void setkey(const char *key); +extern void setkey(const char *key); /* pty functions */ diff --git a/src/system/libroot/posix/stdlib/env.c b/src/system/libroot/posix/stdlib/env.c index a9eefd621a..d486ef2808 100644 --- a/src/system/libroot/posix/stdlib/env.c +++ b/src/system/libroot/posix/stdlib/env.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ @@ -25,15 +25,35 @@ // TODO: Use benaphore! static sem_id sEnvLock; +static char **sManagedEnviron; static bool sCopied; char **environ = NULL; +static inline void +lock_variables(void) +{ + while (acquire_sem(sEnvLock) == B_INTERRUPTED) + ; +} + + +static inline void +unlock_variables(void) +{ + release_sem(sEnvLock); +} + + static int32 count_variables(void) { int32 i = 0; + + if (environ == NULL) + return 0; + while (environ[i]) i++; @@ -52,7 +72,7 @@ add_variable(void) newEnv[count] = NULL; // null terminate the array - environ = newEnv; + environ = sManagedEnviron = newEnv; return count - 1; } @@ -63,6 +83,9 @@ find_variable(const char *name, int32 length, int32 *_index) { int32 i; + if (environ == NULL) + return NULL; + for (i = 0; environ[i] != NULL; i++) { if (!strncmp(name, environ[i], length) && environ[i][length] == '=') { if (_index != NULL) @@ -78,38 +101,44 @@ find_variable(const char *name, int32 length, int32 *_index) static status_t copy_environ_to_heap_if_needed(void) { - char **newEnv; - int32 i; + int32 i = 0; - if (sCopied) + if (sCopied && environ == sManagedEnviron) 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]); + if (sManagedEnviron != NULL) { + // free previously used "environ"; it has been changed by an application + free(sManagedEnviron); } - newEnv[i] = NULL; + sManagedEnviron = malloc((count_variables() + 1) * sizeof(char *)); + if (sManagedEnviron == NULL) + return B_NO_MEMORY; + + if (environ != NULL) { + // copy from previous + for (; environ[i]; i++) { + sManagedEnviron[i] = strdup(environ[i]); + } + } + + sManagedEnviron[i] = NULL; // null terminate the array - environ = newEnv; + environ = sManagedEnviron; sCopied = true; return B_OK; } static status_t -update_variable(const char *name, int32 length, const char *value, bool overwrite) +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); @@ -120,24 +149,23 @@ update_variable(const char *name, int32 length, const char *value, bool overwrit } else if (env == NULL) { // add variable index = add_variable(); - if (index >= 0) - update = true; - else - status = index; + if (index < 0) + return B_NO_MEMORY; + + 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; + if (environ[index] == NULL) + return B_NO_MEMORY; + + memcpy(environ[index], name, length); + environ[index][length] = '='; + strcpy(environ[index] + length + 1, value); } - release_sem(sEnvLock); - return status; + return B_OK; } @@ -148,19 +176,36 @@ environ_fork_hook(void) } -void +// #pragma mark - libroot initializer + + +void __init_env(const struct user_space_program_args *args) { - // Following POSIX, there is no need to make any of the - // environment functions thread-safe - but we do it anyway + // Following POSIX, there is no need to make any of the environment + // functions thread-safe - but we do it anyway as much as possible to + // protect our implementation sEnvLock = create_sem(1, "env lock"); environ = args->env; + sManagedEnviron = NULL; atfork(environ_fork_hook); } -// #pragma mark - +// #pragma mark - public API + + +int +clearenv(void) +{ + lock_variables(); + + free(sManagedEnviron); + sManagedEnviron = environ = NULL; + + unlock_variables(); +} char * @@ -169,9 +214,10 @@ getenv(const char *name) int32 length = strlen(name); char *value; - acquire_sem(sEnvLock); + lock_variables(); + value = find_variable(name, length, NULL); - release_sem(sEnvLock); + unlock_variables(); if (value == NULL) return NULL; @@ -190,7 +236,10 @@ setenv(const char *name, const char *value, int overwrite) return -1; } + lock_variables(); status = update_variable(name, strlen(name), value, overwrite); + unlock_variables(); + RETURN_AND_SET_ERRNO(status); } @@ -208,7 +257,8 @@ unsetenv(const char *name) length = strlen(name); - acquire_sem(sEnvLock); + lock_variables(); + copy_environ_to_heap_if_needed(); env = find_variable(name, length, &index); @@ -220,7 +270,7 @@ unsetenv(const char *name) sizeof(char *) * (count_variables() - index)); } - release_sem(sEnvLock); + unlock_variables(); return 0; } @@ -236,7 +286,10 @@ putenv(const char *string) return -1; } + lock_variables(); status = update_variable(string, value - string, value + 1, true); + unlock_variables(); + RETURN_AND_SET_ERRNO(status); } diff --git a/src/tests/system/libroot/posix/Jamfile b/src/tests/system/libroot/posix/Jamfile index ad9be6c936..694f1de64a 100644 --- a/src/tests/system/libroot/posix/Jamfile +++ b/src/tests/system/libroot/posix/Jamfile @@ -6,6 +6,10 @@ SimpleTest SyslogTest : SyslogTest.cpp syslog.cpp ; +SimpleTest clearenv + : clearenv.cpp +; + SimpleTest flock_test : flock_test.cpp ; diff --git a/src/tests/system/libroot/posix/clearenv.cpp b/src/tests/system/libroot/posix/clearenv.cpp new file mode 100644 index 0000000000..c7c29e0a15 --- /dev/null +++ b/src/tests/system/libroot/posix/clearenv.cpp @@ -0,0 +1,64 @@ +/* + * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + + +#include +#include + + +int gTestNr; + + +void +set_env() +{ + gTestNr++; + printf("Test %d...\n", gTestNr); + + if (setenv("TEST_VARIABLE", "42", true) != 0) + fprintf(stderr, "Test %d: setting variable failed!\n", gTestNr); +} + + +void +test_env() +{ + if (getenv("TEST_VARIABLE") != NULL) + fprintf(stderr, "Test %d: not cleared!\n", gTestNr); + if (setenv("OTHER_VARIABLE", "test", true) != 0) + fprintf(stderr, "Test %d: setting other failed!\n", gTestNr); +} + + +int +main(int argc, char** argv) +{ + set_env(); + environ = NULL; + test_env(); + + set_env(); + environ[0] = NULL; + test_env(); + + static char* emptyEnv[1] = {NULL}; + set_env(); + environ = emptyEnv; + test_env(); + + set_env(); + environ = (char**)calloc(1, sizeof(*environ)); + test_env(); + + // clearenv() is not part of the POSIX specs +#if 1 + set_env(); + clearenv(); + test_env(); +#endif + + return 0; +} +