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