* Sean C. Farley made me aware that some of the possible solutions to clear

the environment would crash on Haiku. Added a small test application that
  just checks every one of those.
* Fixed env locking (in userland, you better check against B_INTERRUPTED).
* Made our code safe against an environ of NULL.
* There is now an additional sManagedEnviron that points to the environment
  our code actually managed; whenever an application overrides environ, we'll
  get aware of it with the next *env() function invocation, and will handle
  it adequately.
* Added non-POSIX clearenv() function.



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26719 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2008-08-01 15:02:03 +00:00
parent 2accd07be4
commit cc6c784ae7
4 changed files with 172 additions and 50 deletions

View File

@ -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 */

View File

@ -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);
}

View File

@ -6,6 +6,10 @@ SimpleTest SyslogTest
: SyslogTest.cpp syslog.cpp
;
SimpleTest clearenv
: clearenv.cpp
;
SimpleTest flock_test
: flock_test.cpp
;

View File

@ -0,0 +1,64 @@
/*
* Copyright 2008, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include <stdio.h>
#include <stdlib.h>
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;
}