1/* 2 * Copyright 2004-2008, Axel D��rfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7#include <errno.h> 8#include <pthread.h> 9#include <stdlib.h> 10#include <string.h> 11 12#include <OS.h> 13 14#include <errno_private.h> 15#include <libroot_private.h> 16#include <locks.h> 17#include <runtime_loader.h> 18#include <syscall_utils.h> 19#include <user_runtime.h> 20 21 22static const char* const kEnvLockName = "env lock"; 23 24static mutex sEnvLock = MUTEX_INITIALIZER(kEnvLockName); 25static char **sManagedEnviron; 26 27char **environ = NULL; 28 29 30static inline void 31lock_variables(void) 32{ 33 mutex_lock(&sEnvLock); 34} 35 36 37static inline void 38unlock_variables(void) 39{ 40 mutex_unlock(&sEnvLock); 41} 42 43 44static void 45free_variables(void) 46{ 47 int32 i; 48 49 if (sManagedEnviron == NULL) 50 return; 51 52 for (i = 0; sManagedEnviron[i] != NULL; i++) { 53 free(sManagedEnviron[i]); 54 } 55 56 free(sManagedEnviron); 57 sManagedEnviron = NULL; 58} 59 60 61static int32 62count_variables(void) 63{ 64 int32 i = 0; 65 66 if (environ == NULL) 67 return 0; 68 69 while (environ[i]) 70 i++; 71 72 return i; 73} 74 75 76static int32 77add_variable(void) 78{ 79 int32 count = count_variables() + 1; 80 char **newEnviron = (char**)realloc(environ, (count + 1) * sizeof(char *)); 81 if (newEnviron == NULL) 82 return B_NO_MEMORY; 83 84 newEnviron[count] = NULL; 85 // null terminate the array 86 87 environ = sManagedEnviron = newEnviron; 88 89 return count - 1; 90} 91 92 93static char * 94find_variable(const char *name, int32 length, int32 *_index) 95{ 96 int32 i; 97 98 if (environ == NULL) 99 return NULL; 100 101 for (i = 0; environ[i] != NULL; i++) { 102 if (!strncmp(name, environ[i], length) && environ[i][length] == '=') { 103 if (_index != NULL) 104 *_index = i; 105 return environ[i]; 106 } 107 } 108 109 return NULL; 110} 111 112 113/*! Copies the environment from its current location into a heap managed 114 environment, if it's not already there. 115 116 This is needed whenever the environment is changed, that is, when one 117 of the POSIX *env() functions is called, and we either used the environment 118 provided by the kernel, or by an application that changed \c environ 119 directly. 120*/ 121static status_t 122copy_environ_to_heap_if_needed(void) 123{ 124 int32 i = 0; 125 126 if (environ == sManagedEnviron) 127 return B_OK; 128 129 // free previously used "environ" if it has been changed by an application 130 free_variables(); 131 132 sManagedEnviron = (char**)malloc((count_variables() + 1) * sizeof(char *)); 133 if (sManagedEnviron == NULL) 134 return B_NO_MEMORY; 135 136 if (environ != NULL) { 137 // copy from previous 138 for (; environ[i]; i++) { 139 sManagedEnviron[i] = strdup(environ[i]); 140 } 141 } 142 143 sManagedEnviron[i] = NULL; 144 // null terminate the array 145 146 environ = sManagedEnviron; 147 return B_OK; 148} 149 150 151static status_t 152update_variable(const char *name, int32 length, const char *value, 153 bool overwrite) 154{ 155 bool update = false; 156 int32 index; 157 char *env; 158 159 copy_environ_to_heap_if_needed(); 160 161 env = find_variable(name, length, &index); 162 if (env != NULL && overwrite) { 163 // change variable 164 free(environ[index]); 165 update = true; 166 } else if (env == NULL) { 167 // add variable 168 index = add_variable(); 169 if (index < 0) 170 return B_NO_MEMORY; 171 172 update = true; 173 } 174 175 if (update) { 176 environ[index] = (char*)malloc(length + 2 + strlen(value)); 177 if (environ[index] == NULL) 178 return B_NO_MEMORY; 179 180 memcpy(environ[index], name, length); 181 environ[index][length] = '='; 182 strcpy(environ[index] + length + 1, value); 183 } 184 185 return B_OK; 186} 187 188 189static void 190environ_fork_hook(void) 191{ 192 mutex_init(&sEnvLock, kEnvLockName); 193} 194 195 196// #pragma mark - libroot initializer 197 198 199void 200__init_env(const struct user_space_program_args *args) 201{ 202 // Following POSIX, there is no need to make any of the environment 203 // functions thread-safe - but we do it anyway as much as possible to 204 // protect our implementation 205 environ = args->env; 206 sManagedEnviron = NULL; 207 208 atfork(environ_fork_hook); 209} 210 211 212// #pragma mark - public API 213 214 215int 216clearenv(void) 217{ 218 lock_variables(); 219 220 free_variables(); 221 environ = NULL; 222 223 unlock_variables(); 224 225 return 0; 226} 227 228 229char * 230getenv(const char *name) 231{ 232 int32 length = strlen(name); 233 char *value; 234 235 lock_variables(); 236 237 value = find_variable(name, length, NULL); 238 unlock_variables(); 239 240 if (value == NULL) 241 return NULL; 242 243 return value + length + 1; 244} 245 246 247int 248setenv(const char *name, const char *value, int overwrite) 249{ 250 status_t status; 251 252 if (name == NULL || name[0] == '\0' || strchr(name, '=') != NULL) { 253 __set_errno(B_BAD_VALUE); 254 return -1; 255 } 256 257 lock_variables(); 258 status = update_variable(name, strlen(name), value, overwrite); 259 unlock_variables(); 260 261 RETURN_AND_SET_ERRNO(status); 262} 263 264 265int 266unsetenv(const char *name) 267{ 268 int32 index, length; 269 char *env; 270 271 if (name == NULL || name[0] == '\0' || strchr(name, '=') != NULL) { 272 __set_errno(B_BAD_VALUE); 273 return -1; 274 } 275 276 length = strlen(name); 277 278 lock_variables(); 279 280 copy_environ_to_heap_if_needed(); 281 282 env = find_variable(name, length, &index); 283 while (env != NULL) { 284 // we don't free the memory for the slot, we just move the array 285 // contents 286 free(env); 287 memmove(environ + index, environ + index + 1, 288 sizeof(char *) * (count_variables() - index)); 289 290 // search possible another occurence, introduced via putenv() 291 // and renamed since 292 env = find_variable(name, length, &index); 293 } 294 295 unlock_variables(); 296 return 0; 297} 298 299 300int 301putenv(const char *string) 302{ 303 char *value = strchr(string, '='); 304 status_t status; 305 306 if (value == NULL) { 307 __set_errno(B_BAD_VALUE); 308 return -1; 309 } 310 311 lock_variables(); 312 status = update_variable(string, value - string, value + 1, true); 313 unlock_variables(); 314 315 RETURN_AND_SET_ERRNO(status); 316} 317 318