* Copyright 2004-2008, Axel DΓΆrfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <OS.h>
#include <errno_private.h>
#include <libroot_private.h>
#include <locks.h>
#include <runtime_loader.h>
#include <stdlib_private.h>
#include <syscall_utils.h>
#include <user_runtime.h>
static const char* const kEnvLockName = "env lock";
static mutex sEnvLock = MUTEX_INITIALIZER(kEnvLockName);
static char **sManagedEnviron;
char **environ = NULL;
static inline void
lock_variables(void)
{
mutex_lock(&sEnvLock);
}
static inline void
unlock_variables(void)
{
mutex_unlock(&sEnvLock);
}
static void
free_variables(void)
{
int32 i;
if (sManagedEnviron == NULL)
return;
for (i = 0; sManagedEnviron[i] != NULL; i++) {
free(sManagedEnviron[i]);
}
free(sManagedEnviron);
sManagedEnviron = NULL;
}
static int32
count_variables(void)
{
int32 i = 0;
if (environ == NULL)
return 0;
while (environ[i])
i++;
return i;
}
static int32
add_variable(void)
{
int32 count = count_variables() + 1;
char **newEnviron = (char**)realloc(environ, (count + 1) * sizeof(char *));
if (newEnviron == NULL)
return B_NO_MEMORY;
newEnviron[count] = NULL;
environ = sManagedEnviron = newEnviron;
return count - 1;
}
static char *
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)
*_index = i;
return environ[i];
}
}
return NULL;
}
environment, if it's not already there.
This is needed whenever the environment is changed, that is, when one
of the POSIX *env() functions is called, and we either used the environment
provided by the kernel, or by an application that changed \c environ
directly.
*/
static status_t
copy_environ_to_heap_if_needed(void)
{
int32 i = 0;
if (environ == sManagedEnviron)
return B_OK;
free_variables();
sManagedEnviron = (char**)malloc((count_variables() + 1) * sizeof(char *));
if (sManagedEnviron == NULL)
return B_NO_MEMORY;
if (environ != NULL) {
for (; environ[i]; i++) {
sManagedEnviron[i] = strdup(environ[i]);
}
}
sManagedEnviron[i] = NULL;
environ = sManagedEnviron;
return B_OK;
}
static status_t
update_variable(const char *name, int32 length, const char *value,
bool overwrite)
{
bool update = false;
int32 index;
char *env;
copy_environ_to_heap_if_needed();
env = find_variable(name, length, &index);
if (env != NULL && overwrite) {
free(environ[index]);
update = true;
} else if (env == NULL) {
index = add_variable();
if (index < 0)
return B_NO_MEMORY;
update = true;
}
if (update) {
environ[index] = (char*)malloc(length + 2 + strlen(value));
if (environ[index] == NULL)
return B_NO_MEMORY;
memcpy(environ[index], name, length);
environ[index][length] = '=';
strcpy(environ[index] + length + 1, value);
}
return B_OK;
}
static void
environ_fork_hook(void)
{
mutex_init(&sEnvLock, kEnvLockName);
}
void
__init_env(const struct user_space_program_args *args)
{
environ = args->env;
sManagedEnviron = NULL;
}
void
__init_env_post_heap()
{
atfork(environ_fork_hook);
}
int
clearenv(void)
{
lock_variables();
free_variables();
environ = NULL;
unlock_variables();
return 0;
}
char *
getenv(const char *name)
{
int32 length = strlen(name);
char *value;
lock_variables();
value = find_variable(name, length, NULL);
unlock_variables();
if (value == NULL)
return NULL;
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) {
__set_errno(B_BAD_VALUE);
return -1;
}
lock_variables();
status = update_variable(name, strlen(name), value, overwrite);
unlock_variables();
RETURN_AND_SET_ERRNO(status);
}
int
unsetenv(const char *name)
{
int32 index, length;
char *env;
if (name == NULL || name[0] == '\0' || strchr(name, '=') != NULL) {
__set_errno(B_BAD_VALUE);
return -1;
}
length = strlen(name);
lock_variables();
copy_environ_to_heap_if_needed();
env = find_variable(name, length, &index);
while (env != NULL) {
free(env);
memmove(environ + index, environ + index + 1,
sizeof(char *) * (count_variables() - index));
env = find_variable(name, length, &index);
}
unlock_variables();
return 0;
}
int
putenv(char *string)
{
char *value = strchr(string, '=');
status_t status;
if (value == NULL || value == string) {
__set_errno(B_BAD_VALUE);
return -1;
}
lock_variables();
status = update_variable(string, value - string, value + 1, true);
unlock_variables();
RETURN_AND_SET_ERRNO(status);
}
ssize_t
__getenv_reentrant(const char* name, char* buffer, size_t bufferSize)
{
size_t nameLength = strlen(name);
lock_variables();
char* value = find_variable(name, nameLength, NULL);
ssize_t result = value != NULL
? strlcpy(buffer, value + nameLength + 1, bufferSize)
: B_NAME_NOT_FOUND;
unlock_variables();
return result;
}