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