1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* Copyright (c) 1988 AT&T */ 27 /* All Rights Reserved */ 28 29 #pragma weak _putenv = putenv 30 31 #include "lint.h" 32 #include <mtlib.h> 33 #include <sys/types.h> 34 #include <thread.h> 35 #include <synch.h> 36 #include <stdlib.h> 37 #include <errno.h> 38 #include <string.h> 39 #include <atomic.h> 40 41 #define MIN_ENV_SIZE 128 42 43 extern const char **_environ; 44 extern void clean_env(); 45 46 /* 47 * For performance and consistency reasons we expand the _environ list using 48 * the trusted "power of two, drop it on the floor" method. This allows for 49 * a lockless, single pass implementation of getenv(), yet the memory leak 50 * is bounded - in normal circumstances total wastage is never greater than 51 * 3x the space needed to hold any _environ list. 52 * 53 * The only abnormal circumstance is if an application modifies the _environ 54 * list pointer directly. Such an application does not conform to POSIX.1 55 * 2001. However, we also care about standards which did not foresee this 56 * issue. For this reason we keep a working copy of our notion of _environ in 57 * my_environ. If, when we are called upon to modify _environ, we ever detect 58 * a mismatch between _environ and my_environ we discard all our assumptions 59 * concerning the location and size of the _environ list. As an additional 60 * precaution we only ever update _environ once we have finished manipulating 61 * our working copy. 62 * 63 * The setenv() API is inherently leaky but we are completely at the mercy 64 * of the application. 65 * 66 * To pacify leak detectors we chain all allocations which are at risk of 67 * being leaked in either of the above two scenarios. chunk_list must only 68 * be updated under the protection of update_lock. 69 * 70 * Although we don't allocate the original _environ list it is likely that 71 * we will leak this too. Accordingly, we create a reference in initenv(). 72 * However, we can't be held responsible for such leaks in abnormal (see 73 * above) circumstances. 74 */ 75 76 typedef struct chunk { 77 struct chunk *next; 78 } chunk_t; 79 80 static mutex_t update_lock = DEFAULTMUTEX; 81 static const char **orig_environ = NULL; 82 static const char **my_environ = NULL; 83 static const char **environ_base = NULL; 84 static int environ_size = 0; 85 static int environ_gen = 0; 86 static int initenv_done = 0; 87 static chunk_t *chunk_list = NULL; 88 89 /* 90 * Compute the size an _environ list including the terminating NULL entry. 91 * This is the only way we have to determine the size of an _environ list 92 * we didn't allocate. 93 */ 94 static int 95 envsize(const char **e) 96 { 97 int size; 98 99 if (e == NULL) 100 return (0); 101 102 for (size = 1; *e != NULL; e++) 103 size++; 104 105 return (size); 106 } 107 108 /* 109 * Initialization for the following scenarios: 110 * 1. The very first time we reference the _environ list we must call in the 111 * NLSPATH janitor, make a reference to the original _environ list to keep 112 * leak detectors happy, initialize my_environ and environ_base, and then 113 * compute environ_size. 114 * 2. Whenever we detect that someone else has hijacked _environ (something 115 * very abnormal) we need to reinitialize my_environ and environ_base, 116 * and then recompute environ_size. 117 * 118 * The local globals my_environ, environ_base and environ_size may be used 119 * by others only if initenv_done is true and only under the protection of 120 * update_lock. However, our callers, who must NOT be holding update_lock, 121 * may safely test initenv_done or my_environ against _environ just prior to 122 * calling us because we test these again whilst holding update_lock. 123 */ 124 static void 125 initenv() 126 { 127 if ((my_environ != _environ) || !initenv_done) { 128 lmutex_lock(&update_lock); 129 if ((my_environ != _environ) || !initenv_done) { 130 if (!initenv_done) { 131 /* Call the NLSPATH janitor in. */ 132 clean_env(); 133 134 /* Pacify leak detectors in normal operation. */ 135 orig_environ = _environ; 136 #ifdef __lint 137 my_environ = orig_environ; 138 #endif 139 } 140 141 my_environ = _environ; 142 environ_base = my_environ; 143 environ_size = envsize(environ_base); 144 membar_producer(); 145 initenv_done = 1; 146 } 147 lmutex_unlock(&update_lock); 148 } 149 membar_consumer(); 150 } 151 152 /* 153 * Search an _environ list for a particular entry. If name_only is set, then 154 * string must be the entry name only, and we return the value of the first 155 * match. Otherwise, string must be of the form "name=value", and we return 156 * the address of the first matching entry. 157 */ 158 static const char ** 159 findenv(const char **e, const char *string, int name_only, char **value) 160 { 161 char target; 162 const char *s1; 163 const char *s2; 164 165 *value = NULL; 166 167 if (e == NULL) 168 return (NULL); 169 170 target = name_only ? '\0' : '='; 171 172 for (; (s2 = *e) != NULL; e++) { 173 s1 = string; 174 175 /* Fast comparison for first char. */ 176 if (*s1 != *s2) 177 continue; 178 179 /* Slow comparison for rest of string. */ 180 while (*s1 == *s2 && *s2 != '=') { 181 s1++; 182 s2++; 183 } 184 185 if (*s1 == target && *s2 == '=') { 186 *value = (char *)s2 + 1; 187 return (e); 188 } 189 } 190 return (NULL); 191 } 192 193 /* 194 * Common code for putenv() and setenv(). We support the lockless getenv() 195 * by inserting new entries at the bottom of the list, and by growing the 196 * list using the trusted "power of two, drop it on the floor" method. We 197 * use a lock (update_lock) to protect all updates to the _environ list, but 198 * we are obliged to release this lock whenever we call malloc() or free(). 199 * A generation number (environ_gen) is bumped whenever names are added to, 200 * or removed from, the _environ list so that we can detect collisions with 201 * other updaters. 202 * 203 * Return values 204 * 0 : success 205 * -1 : with errno set 206 * -2 : an entry already existed and overwrite was zero 207 */ 208 static int 209 addtoenv(char *string, int overwrite) 210 { 211 char *value; 212 const char **p; 213 chunk_t *new_chunk; 214 const char **new_environ; 215 const char **new_base; 216 int new_size; 217 int old_gen; 218 219 initenv(); 220 221 lmutex_lock(&update_lock); 222 223 for (;;) { 224 /* 225 * If the name already exists just overwrite the existing 226 * entry -- except when we were called by setenv() without 227 * the overwrite flag. 228 */ 229 if ((p = findenv(my_environ, string, 0, &value)) != NULL) { 230 if (overwrite) { 231 /* 232 * Replace the value in situ. No name was 233 * added, so there is no need to bump the 234 * generation number. 235 */ 236 *p = string; 237 lmutex_unlock(&update_lock); 238 return (0); 239 } else { 240 /* No change. */ 241 lmutex_unlock(&update_lock); 242 return (-2); 243 } 244 } 245 246 /* Try to insert the new entry at the bottom of the list. */ 247 if (environ_base < my_environ) { 248 /* 249 * The new value must be visible before we decrement 250 * the _environ list pointer. 251 */ 252 my_environ[-1] = string; 253 membar_producer(); 254 my_environ--; 255 _environ = my_environ; 256 257 /* 258 * We've added a name, so bump the generation number. 259 */ 260 environ_gen++; 261 262 lmutex_unlock(&update_lock); 263 return (0); 264 } 265 266 /* 267 * There is no room. Attempt to allocate a new _environ list 268 * which is at least double the size of the current one. See 269 * comment above concerning locking and malloc() etc. 270 */ 271 new_size = environ_size * 2; 272 if (new_size < MIN_ENV_SIZE) 273 new_size = MIN_ENV_SIZE; 274 275 old_gen = environ_gen; 276 lmutex_unlock(&update_lock); 277 278 new_chunk = malloc(sizeof (chunk_t) + 279 new_size * sizeof (char *)); 280 if (new_chunk == NULL) { 281 errno = ENOMEM; 282 return (-1); 283 } 284 285 lmutex_lock(&update_lock); 286 287 /* 288 * If no other thread added or removed names while the lock 289 * was dropped, it is time to break out of this loop. 290 */ 291 if (environ_gen == old_gen) 292 break; 293 294 /* 295 * At least one name has been added or removed, so we need to 296 * try again. It is very likely that we will find sufficient 297 * space the next time around. 298 */ 299 lmutex_unlock(&update_lock); 300 free(new_chunk); 301 lmutex_lock(&update_lock); 302 } 303 304 /* Add the new chunk to chunk_list to hide potential future leak. */ 305 new_chunk->next = chunk_list; 306 chunk_list = new_chunk; 307 308 /* Copy the old _environ list into the top of the new _environ list. */ 309 new_base = (const char **)(new_chunk + 1); 310 new_environ = &new_base[(new_size - 1) - environ_size]; 311 (void) memcpy(new_environ, my_environ, environ_size * sizeof (char *)); 312 313 /* Insert the new entry at the bottom of the new _environ list. */ 314 new_environ[-1] = string; 315 new_environ--; 316 317 /* Ensure that the new _environ list is visible to all. */ 318 membar_producer(); 319 320 /* Make the switch (dropping the old _environ list on the floor). */ 321 environ_base = new_base; 322 my_environ = new_environ; 323 _environ = my_environ; 324 environ_size = new_size; 325 326 /* We've added a name, so bump the generation number. */ 327 environ_gen++; 328 329 lmutex_unlock(&update_lock); 330 return (0); 331 } 332 333 /* 334 * All the work for putenv() is done in addtoenv(). 335 */ 336 int 337 putenv(char *string) 338 { 339 return (addtoenv(string, 1)); 340 } 341 342 /* 343 * setenv() is a little more complex than putenv() because we have to allocate 344 * and construct an _environ entry on behalf of the caller. The bulk of the 345 * work is still done in addtoenv(). 346 */ 347 348 int 349 setenv(const char *envname, const char *envval, int overwrite) 350 { 351 chunk_t *new_chunk; 352 char *new_string; 353 size_t name_len; 354 size_t val_len; 355 int res; 356 357 if (envname == NULL || *envname == 0 || strchr(envname, '=') != NULL) { 358 errno = EINVAL; 359 return (-1); 360 } 361 362 name_len = strlen(envname); 363 val_len = strlen(envval); 364 365 new_chunk = malloc(sizeof (chunk_t) + name_len + val_len + 2); 366 if (new_chunk == NULL) { 367 errno = ENOMEM; 368 return (-1); 369 } 370 new_string = (char *)(new_chunk + 1); 371 372 (void) memcpy(new_string, envname, name_len); 373 new_string[name_len] = '='; 374 (void) memcpy(new_string + name_len + 1, envval, val_len); 375 new_string[name_len + 1 + val_len] = 0; 376 377 if ((res = addtoenv(new_string, overwrite)) < 0) { 378 free(new_chunk); 379 if (res == -2) { 380 /* The name already existed, but not an error. */ 381 return (0); 382 } else { 383 /* i.e. res == -1 which means only one thing. */ 384 errno = ENOMEM; 385 return (-1); 386 } 387 } 388 389 /* Hide potential leak of new_string. */ 390 lmutex_lock(&update_lock); 391 new_chunk->next = chunk_list; 392 chunk_list = new_chunk; 393 lmutex_unlock(&update_lock); 394 395 return (0); 396 } 397 398 /* 399 * unsetenv() is tricky because we need to compress the _environ list in a way 400 * which supports a lockless getenv(). The approach here is to move the first 401 * entry from the enrivon list into the space occupied by the entry to be 402 * deleted, and then to increment _environ. This has the added advantage of 403 * making _any_ incremental linear search of the _environ list consistent (i.e. 404 * we will not break any naughty apps which read the list without our help). 405 */ 406 int 407 unsetenv(const char *name) 408 { 409 const char **p; 410 char *value; 411 412 if (name == NULL || *name == 0 || strchr(name, '=') != NULL) { 413 errno = EINVAL; 414 return (-1); 415 } 416 417 initenv(); 418 419 lmutex_lock(&update_lock); 420 421 /* 422 * Find the target, overwrite it with the first entry, increment the 423 * _environ pointer. 424 */ 425 if ((p = findenv(my_environ, name, 1, &value)) != NULL) { 426 /* Overwrite target with the first entry. */ 427 *p = my_environ[0]; 428 429 /* Ensure that the moved entry is visible to all. */ 430 membar_producer(); 431 432 /* Shrink the _environ list. */ 433 my_environ++; 434 _environ = my_environ; 435 436 /* Make sure addtoenv() knows that we've removed a name. */ 437 environ_gen++; 438 } 439 440 lmutex_unlock(&update_lock); 441 return (0); 442 } 443 444 /* 445 * Dump entire environment. 446 */ 447 int 448 clearenv(void) 449 { 450 /* 451 * Just drop the entire environment list on the floor, as it 452 * would be non-trivial to try and free the used memory. 453 */ 454 static const char *nullp = NULL; 455 456 lmutex_lock(&update_lock); 457 _environ = &nullp; 458 my_environ = NULL; 459 environ_base = NULL; 460 environ_size = 0; 461 environ_gen++; 462 membar_producer(); 463 lmutex_unlock(&update_lock); 464 465 return (0); 466 } 467 468 /* 469 * At last, a lockless implementation of getenv()! 470 */ 471 char * 472 getenv(const char *name) 473 { 474 char *value; 475 476 initenv(); 477 478 if (findenv(_environ, name, 1, &value) != NULL) 479 return (value); 480 481 return (NULL); 482 } 483