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 22 static const char* const kEnvLockName = "env lock"; 23 24 static mutex sEnvLock = MUTEX_INITIALIZER(kEnvLockName); 25 static char **sManagedEnviron; 26 27 char **environ = NULL; 28 29 30 static inline void 31 lock_variables(void) 32 { 33 mutex_lock(&sEnvLock); 34 } 35 36 37 static inline void 38 unlock_variables(void) 39 { 40 mutex_unlock(&sEnvLock); 41 } 42 43 44 static void 45 free_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 61 static int32 62 count_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 76 static int32 77 add_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 93 static char * 94 find_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 */ 121 static status_t 122 copy_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 151 static status_t 152 update_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 189 static void 190 environ_fork_hook(void) 191 { 192 mutex_init(&sEnvLock, kEnvLockName); 193 } 194 195 196 // #pragma mark - libroot initializer 197 198 199 void 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 215 int 216 clearenv(void) 217 { 218 lock_variables(); 219 220 free_variables(); 221 environ = NULL; 222 223 unlock_variables(); 224 225 return 0; 226 } 227 228 229 char * 230 getenv(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 247 int 248 setenv(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 265 int 266 unsetenv(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 300 int 301 putenv(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