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