17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5cb620785Sraf * Common Development and Distribution License (the "License"). 6cb620785Sraf * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 21cb620785Sraf 227c478bd9Sstevel@tonic-gate /* 23*23a1cceaSRoger A. Faulkner * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */ 277c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 287c478bd9Sstevel@tonic-gate 297257d1b4Sraf #pragma weak _putenv = putenv 307c478bd9Sstevel@tonic-gate 317257d1b4Sraf #include "lint.h" 327c478bd9Sstevel@tonic-gate #include <mtlib.h> 337c478bd9Sstevel@tonic-gate #include <sys/types.h> 347c478bd9Sstevel@tonic-gate #include <thread.h> 357c478bd9Sstevel@tonic-gate #include <synch.h> 367c478bd9Sstevel@tonic-gate #include <stdlib.h> 377c478bd9Sstevel@tonic-gate #include <errno.h> 387c478bd9Sstevel@tonic-gate #include <string.h> 397c478bd9Sstevel@tonic-gate #include <atomic.h> 407c478bd9Sstevel@tonic-gate 417c478bd9Sstevel@tonic-gate #define MIN_ENV_SIZE 128 427c478bd9Sstevel@tonic-gate 4359f081edSraf extern const char **_environ; 447c478bd9Sstevel@tonic-gate extern void clean_env(); 457c478bd9Sstevel@tonic-gate 467c478bd9Sstevel@tonic-gate /* 4759f081edSraf * For performance and consistency reasons we expand the _environ list using 487c478bd9Sstevel@tonic-gate * the trusted "power of two, drop it on the floor" method. This allows for 497c478bd9Sstevel@tonic-gate * a lockless, single pass implementation of getenv(), yet the memory leak 507c478bd9Sstevel@tonic-gate * is bounded - in normal circumstances total wastage is never greater than 5159f081edSraf * 3x the space needed to hold any _environ list. 527c478bd9Sstevel@tonic-gate * 5359f081edSraf * The only abnormal circumstance is if an application modifies the _environ 547c478bd9Sstevel@tonic-gate * list pointer directly. Such an application does not conform to POSIX.1 557c478bd9Sstevel@tonic-gate * 2001. However, we also care about standards which did not foresee this 5659f081edSraf * issue. For this reason we keep a working copy of our notion of _environ in 5759f081edSraf * my_environ. If, when we are called upon to modify _environ, we ever detect 5859f081edSraf * a mismatch between _environ and my_environ we discard all our assumptions 5959f081edSraf * concerning the location and size of the _environ list. As an additional 6059f081edSraf * precaution we only ever update _environ once we have finished manipulating 617c478bd9Sstevel@tonic-gate * our working copy. 627c478bd9Sstevel@tonic-gate * 637c478bd9Sstevel@tonic-gate * The setenv() API is inherently leaky but we are completely at the mercy 647c478bd9Sstevel@tonic-gate * of the application. 657c478bd9Sstevel@tonic-gate * 667c478bd9Sstevel@tonic-gate * To pacify leak detectors we chain all allocations which are at risk of 677c478bd9Sstevel@tonic-gate * being leaked in either of the above two scenarios. chunk_list must only 687c478bd9Sstevel@tonic-gate * be updated under the protection of update_lock. 697c478bd9Sstevel@tonic-gate * 7059f081edSraf * Although we don't allocate the original _environ list it is likely that 717c478bd9Sstevel@tonic-gate * we will leak this too. Accordingly, we create a reference in initenv(). 727c478bd9Sstevel@tonic-gate * However, we can't be held responsible for such leaks in abnormal (see 737c478bd9Sstevel@tonic-gate * above) circumstances. 747c478bd9Sstevel@tonic-gate */ 757c478bd9Sstevel@tonic-gate 767c478bd9Sstevel@tonic-gate typedef struct chunk { 777c478bd9Sstevel@tonic-gate struct chunk *next; 787c478bd9Sstevel@tonic-gate } chunk_t; 797c478bd9Sstevel@tonic-gate 807c478bd9Sstevel@tonic-gate static mutex_t update_lock = DEFAULTMUTEX; 817c478bd9Sstevel@tonic-gate static const char **orig_environ = NULL; 827c478bd9Sstevel@tonic-gate static const char **my_environ = NULL; 837c478bd9Sstevel@tonic-gate static const char **environ_base = NULL; 847c478bd9Sstevel@tonic-gate static int environ_size = 0; 857c478bd9Sstevel@tonic-gate static int environ_gen = 0; 867c478bd9Sstevel@tonic-gate static int initenv_done = 0; 877c478bd9Sstevel@tonic-gate static chunk_t *chunk_list = NULL; 887c478bd9Sstevel@tonic-gate 897c478bd9Sstevel@tonic-gate /* 9059f081edSraf * Compute the size an _environ list including the terminating NULL entry. 9159f081edSraf * This is the only way we have to determine the size of an _environ list 927c478bd9Sstevel@tonic-gate * we didn't allocate. 937c478bd9Sstevel@tonic-gate */ 947c478bd9Sstevel@tonic-gate static int 957c478bd9Sstevel@tonic-gate envsize(const char **e) 967c478bd9Sstevel@tonic-gate { 977c478bd9Sstevel@tonic-gate int size; 987c478bd9Sstevel@tonic-gate 997c478bd9Sstevel@tonic-gate if (e == NULL) 1007c478bd9Sstevel@tonic-gate return (0); 1017c478bd9Sstevel@tonic-gate 1027c478bd9Sstevel@tonic-gate for (size = 1; *e != NULL; e++) 1037c478bd9Sstevel@tonic-gate size++; 1047c478bd9Sstevel@tonic-gate 1057c478bd9Sstevel@tonic-gate return (size); 1067c478bd9Sstevel@tonic-gate } 1077c478bd9Sstevel@tonic-gate 1087c478bd9Sstevel@tonic-gate /* 1097c478bd9Sstevel@tonic-gate * Initialization for the following scenarios: 11059f081edSraf * 1. The very first time we reference the _environ list we must call in the 11159f081edSraf * NLSPATH janitor, make a reference to the original _environ list to keep 1127c478bd9Sstevel@tonic-gate * leak detectors happy, initialize my_environ and environ_base, and then 1137c478bd9Sstevel@tonic-gate * compute environ_size. 11459f081edSraf * 2. Whenever we detect that someone else has hijacked _environ (something 1157c478bd9Sstevel@tonic-gate * very abnormal) we need to reinitialize my_environ and environ_base, 1167c478bd9Sstevel@tonic-gate * and then recompute environ_size. 1177c478bd9Sstevel@tonic-gate * 1187c478bd9Sstevel@tonic-gate * The local globals my_environ, environ_base and environ_size may be used 1197c478bd9Sstevel@tonic-gate * by others only if initenv_done is true and only under the protection of 1207c478bd9Sstevel@tonic-gate * update_lock. However, our callers, who must NOT be holding update_lock, 12159f081edSraf * may safely test initenv_done or my_environ against _environ just prior to 1227c478bd9Sstevel@tonic-gate * calling us because we test these again whilst holding update_lock. 1237c478bd9Sstevel@tonic-gate */ 1247c478bd9Sstevel@tonic-gate static void 1257c478bd9Sstevel@tonic-gate initenv() 1267c478bd9Sstevel@tonic-gate { 12759f081edSraf if ((my_environ != _environ) || !initenv_done) { 128cb620785Sraf lmutex_lock(&update_lock); 12959f081edSraf if ((my_environ != _environ) || !initenv_done) { 130cb620785Sraf if (!initenv_done) { 131cb620785Sraf /* Call the NLSPATH janitor in. */ 132cb620785Sraf clean_env(); 1337c478bd9Sstevel@tonic-gate 134cb620785Sraf /* Pacify leak detectors in normal operation. */ 13559f081edSraf orig_environ = _environ; 1367c478bd9Sstevel@tonic-gate #ifdef __lint 137cb620785Sraf my_environ = orig_environ; 1387c478bd9Sstevel@tonic-gate #endif 139cb620785Sraf } 1407c478bd9Sstevel@tonic-gate 14159f081edSraf my_environ = _environ; 142cb620785Sraf environ_base = my_environ; 143cb620785Sraf environ_size = envsize(environ_base); 144cb620785Sraf membar_producer(); 1457c478bd9Sstevel@tonic-gate initenv_done = 1; 1467c478bd9Sstevel@tonic-gate } 147cb620785Sraf lmutex_unlock(&update_lock); 1487c478bd9Sstevel@tonic-gate } 149cb620785Sraf membar_consumer(); 1507c478bd9Sstevel@tonic-gate } 1517c478bd9Sstevel@tonic-gate 1527c478bd9Sstevel@tonic-gate /* 15359f081edSraf * Search an _environ list for a particular entry. If name_only is set, then 1547c478bd9Sstevel@tonic-gate * string must be the entry name only, and we return the value of the first 1557c478bd9Sstevel@tonic-gate * match. Otherwise, string must be of the form "name=value", and we return 1567c478bd9Sstevel@tonic-gate * the address of the first matching entry. 1577c478bd9Sstevel@tonic-gate */ 1587c478bd9Sstevel@tonic-gate static const char ** 1597c478bd9Sstevel@tonic-gate findenv(const char **e, const char *string, int name_only, char **value) 1607c478bd9Sstevel@tonic-gate { 1617c478bd9Sstevel@tonic-gate char target; 1627c478bd9Sstevel@tonic-gate const char *s1; 1637c478bd9Sstevel@tonic-gate const char *s2; 1647c478bd9Sstevel@tonic-gate 1657c478bd9Sstevel@tonic-gate *value = NULL; 1667c478bd9Sstevel@tonic-gate 1677c478bd9Sstevel@tonic-gate if (e == NULL) 1687c478bd9Sstevel@tonic-gate return (NULL); 1697c478bd9Sstevel@tonic-gate 1707c478bd9Sstevel@tonic-gate target = name_only ? '\0' : '='; 1717c478bd9Sstevel@tonic-gate 1727c478bd9Sstevel@tonic-gate for (; (s2 = *e) != NULL; e++) { 1737c478bd9Sstevel@tonic-gate s1 = string; 1747c478bd9Sstevel@tonic-gate 1757c478bd9Sstevel@tonic-gate /* Fast comparison for first char. */ 1767c478bd9Sstevel@tonic-gate if (*s1 != *s2) 1777c478bd9Sstevel@tonic-gate continue; 1787c478bd9Sstevel@tonic-gate 1797c478bd9Sstevel@tonic-gate /* Slow comparison for rest of string. */ 1807c478bd9Sstevel@tonic-gate while (*s1 == *s2 && *s2 != '=') { 1817c478bd9Sstevel@tonic-gate s1++; 1827c478bd9Sstevel@tonic-gate s2++; 1837c478bd9Sstevel@tonic-gate } 1847c478bd9Sstevel@tonic-gate 1857c478bd9Sstevel@tonic-gate if (*s1 == target && *s2 == '=') { 1867c478bd9Sstevel@tonic-gate *value = (char *)s2 + 1; 1877c478bd9Sstevel@tonic-gate return (e); 1887c478bd9Sstevel@tonic-gate } 1897c478bd9Sstevel@tonic-gate } 1907c478bd9Sstevel@tonic-gate return (NULL); 1917c478bd9Sstevel@tonic-gate } 1927c478bd9Sstevel@tonic-gate 1937c478bd9Sstevel@tonic-gate /* 1947c478bd9Sstevel@tonic-gate * Common code for putenv() and setenv(). We support the lockless getenv() 1957c478bd9Sstevel@tonic-gate * by inserting new entries at the bottom of the list, and by growing the 1967c478bd9Sstevel@tonic-gate * list using the trusted "power of two, drop it on the floor" method. We 19759f081edSraf * use a lock (update_lock) to protect all updates to the _environ list, but 1987c478bd9Sstevel@tonic-gate * we are obliged to release this lock whenever we call malloc() or free(). 1997c478bd9Sstevel@tonic-gate * A generation number (environ_gen) is bumped whenever names are added to, 20059f081edSraf * or removed from, the _environ list so that we can detect collisions with 2017c478bd9Sstevel@tonic-gate * other updaters. 2027c478bd9Sstevel@tonic-gate * 2037c478bd9Sstevel@tonic-gate * Return values 2047c478bd9Sstevel@tonic-gate * 0 : success 2057c478bd9Sstevel@tonic-gate * -1 : with errno set 2067c478bd9Sstevel@tonic-gate * -2 : an entry already existed and overwrite was zero 2077c478bd9Sstevel@tonic-gate */ 2087c478bd9Sstevel@tonic-gate static int 2097c478bd9Sstevel@tonic-gate addtoenv(char *string, int overwrite) 2107c478bd9Sstevel@tonic-gate { 2117c478bd9Sstevel@tonic-gate char *value; 2127c478bd9Sstevel@tonic-gate const char **p; 2137c478bd9Sstevel@tonic-gate chunk_t *new_chunk; 2147c478bd9Sstevel@tonic-gate const char **new_environ; 2157c478bd9Sstevel@tonic-gate const char **new_base; 2167c478bd9Sstevel@tonic-gate int new_size; 2177c478bd9Sstevel@tonic-gate int old_gen; 2187c478bd9Sstevel@tonic-gate 219cb620785Sraf initenv(); 2207c478bd9Sstevel@tonic-gate 2217c478bd9Sstevel@tonic-gate lmutex_lock(&update_lock); 2227c478bd9Sstevel@tonic-gate 2237c478bd9Sstevel@tonic-gate for (;;) { 2247c478bd9Sstevel@tonic-gate /* 2257c478bd9Sstevel@tonic-gate * If the name already exists just overwrite the existing 2267c478bd9Sstevel@tonic-gate * entry -- except when we were called by setenv() without 2277c478bd9Sstevel@tonic-gate * the overwrite flag. 2287c478bd9Sstevel@tonic-gate */ 2297c478bd9Sstevel@tonic-gate if ((p = findenv(my_environ, string, 0, &value)) != NULL) { 2307c478bd9Sstevel@tonic-gate if (overwrite) { 2317c478bd9Sstevel@tonic-gate /* 2327c478bd9Sstevel@tonic-gate * Replace the value in situ. No name was 2337c478bd9Sstevel@tonic-gate * added, so there is no need to bump the 2347c478bd9Sstevel@tonic-gate * generation number. 2357c478bd9Sstevel@tonic-gate */ 2367c478bd9Sstevel@tonic-gate *p = string; 2377c478bd9Sstevel@tonic-gate lmutex_unlock(&update_lock); 2387c478bd9Sstevel@tonic-gate return (0); 2397c478bd9Sstevel@tonic-gate } else { 2407c478bd9Sstevel@tonic-gate /* No change. */ 2417c478bd9Sstevel@tonic-gate lmutex_unlock(&update_lock); 2427c478bd9Sstevel@tonic-gate return (-2); 2437c478bd9Sstevel@tonic-gate } 2447c478bd9Sstevel@tonic-gate } 2457c478bd9Sstevel@tonic-gate 2467c478bd9Sstevel@tonic-gate /* Try to insert the new entry at the bottom of the list. */ 2477c478bd9Sstevel@tonic-gate if (environ_base < my_environ) { 2487c478bd9Sstevel@tonic-gate /* 2497c478bd9Sstevel@tonic-gate * The new value must be visible before we decrement 25059f081edSraf * the _environ list pointer. 2517c478bd9Sstevel@tonic-gate */ 2527c478bd9Sstevel@tonic-gate my_environ[-1] = string; 2537c478bd9Sstevel@tonic-gate membar_producer(); 2547c478bd9Sstevel@tonic-gate my_environ--; 25559f081edSraf _environ = my_environ; 2567c478bd9Sstevel@tonic-gate 2577c478bd9Sstevel@tonic-gate /* 2587c478bd9Sstevel@tonic-gate * We've added a name, so bump the generation number. 2597c478bd9Sstevel@tonic-gate */ 2607c478bd9Sstevel@tonic-gate environ_gen++; 2617c478bd9Sstevel@tonic-gate 2627c478bd9Sstevel@tonic-gate lmutex_unlock(&update_lock); 2637c478bd9Sstevel@tonic-gate return (0); 2647c478bd9Sstevel@tonic-gate } 2657c478bd9Sstevel@tonic-gate 2667c478bd9Sstevel@tonic-gate /* 26759f081edSraf * There is no room. Attempt to allocate a new _environ list 2687c478bd9Sstevel@tonic-gate * which is at least double the size of the current one. See 2697c478bd9Sstevel@tonic-gate * comment above concerning locking and malloc() etc. 2707c478bd9Sstevel@tonic-gate */ 2717c478bd9Sstevel@tonic-gate new_size = environ_size * 2; 2727c478bd9Sstevel@tonic-gate if (new_size < MIN_ENV_SIZE) 2737c478bd9Sstevel@tonic-gate new_size = MIN_ENV_SIZE; 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate old_gen = environ_gen; 2767c478bd9Sstevel@tonic-gate lmutex_unlock(&update_lock); 2777c478bd9Sstevel@tonic-gate 2787c478bd9Sstevel@tonic-gate new_chunk = malloc(sizeof (chunk_t) + 2797c478bd9Sstevel@tonic-gate new_size * sizeof (char *)); 2807c478bd9Sstevel@tonic-gate if (new_chunk == NULL) { 2817c478bd9Sstevel@tonic-gate errno = ENOMEM; 2827c478bd9Sstevel@tonic-gate return (-1); 2837c478bd9Sstevel@tonic-gate } 2847c478bd9Sstevel@tonic-gate 2857c478bd9Sstevel@tonic-gate lmutex_lock(&update_lock); 2867c478bd9Sstevel@tonic-gate 2877c478bd9Sstevel@tonic-gate /* 2887c478bd9Sstevel@tonic-gate * If no other thread added or removed names while the lock 2897c478bd9Sstevel@tonic-gate * was dropped, it is time to break out of this loop. 2907c478bd9Sstevel@tonic-gate */ 2917c478bd9Sstevel@tonic-gate if (environ_gen == old_gen) 2927c478bd9Sstevel@tonic-gate break; 2937c478bd9Sstevel@tonic-gate 2947c478bd9Sstevel@tonic-gate /* 2957c478bd9Sstevel@tonic-gate * At least one name has been added or removed, so we need to 2967c478bd9Sstevel@tonic-gate * try again. It is very likely that we will find sufficient 2977c478bd9Sstevel@tonic-gate * space the next time around. 2987c478bd9Sstevel@tonic-gate */ 2997c478bd9Sstevel@tonic-gate lmutex_unlock(&update_lock); 3007c478bd9Sstevel@tonic-gate free(new_chunk); 3017c478bd9Sstevel@tonic-gate lmutex_lock(&update_lock); 3027c478bd9Sstevel@tonic-gate } 3037c478bd9Sstevel@tonic-gate 3047c478bd9Sstevel@tonic-gate /* Add the new chunk to chunk_list to hide potential future leak. */ 3057c478bd9Sstevel@tonic-gate new_chunk->next = chunk_list; 3067c478bd9Sstevel@tonic-gate chunk_list = new_chunk; 3077c478bd9Sstevel@tonic-gate 30859f081edSraf /* Copy the old _environ list into the top of the new _environ list. */ 3097c478bd9Sstevel@tonic-gate new_base = (const char **)(new_chunk + 1); 3107c478bd9Sstevel@tonic-gate new_environ = &new_base[(new_size - 1) - environ_size]; 3117c478bd9Sstevel@tonic-gate (void) memcpy(new_environ, my_environ, environ_size * sizeof (char *)); 3127c478bd9Sstevel@tonic-gate 31359f081edSraf /* Insert the new entry at the bottom of the new _environ list. */ 3147c478bd9Sstevel@tonic-gate new_environ[-1] = string; 3157c478bd9Sstevel@tonic-gate new_environ--; 3167c478bd9Sstevel@tonic-gate 31759f081edSraf /* Ensure that the new _environ list is visible to all. */ 3187c478bd9Sstevel@tonic-gate membar_producer(); 3197c478bd9Sstevel@tonic-gate 32059f081edSraf /* Make the switch (dropping the old _environ list on the floor). */ 3217c478bd9Sstevel@tonic-gate environ_base = new_base; 3227c478bd9Sstevel@tonic-gate my_environ = new_environ; 32359f081edSraf _environ = my_environ; 3247c478bd9Sstevel@tonic-gate environ_size = new_size; 3257c478bd9Sstevel@tonic-gate 3267c478bd9Sstevel@tonic-gate /* We've added a name, so bump the generation number. */ 3277c478bd9Sstevel@tonic-gate environ_gen++; 3287c478bd9Sstevel@tonic-gate 3297c478bd9Sstevel@tonic-gate lmutex_unlock(&update_lock); 3307c478bd9Sstevel@tonic-gate return (0); 3317c478bd9Sstevel@tonic-gate } 3327c478bd9Sstevel@tonic-gate 3337c478bd9Sstevel@tonic-gate /* 3347c478bd9Sstevel@tonic-gate * All the work for putenv() is done in addtoenv(). 3357c478bd9Sstevel@tonic-gate */ 3367c478bd9Sstevel@tonic-gate int 3377c478bd9Sstevel@tonic-gate putenv(char *string) 3387c478bd9Sstevel@tonic-gate { 3397c478bd9Sstevel@tonic-gate return (addtoenv(string, 1)); 3407c478bd9Sstevel@tonic-gate } 3417c478bd9Sstevel@tonic-gate 3427c478bd9Sstevel@tonic-gate /* 3437c478bd9Sstevel@tonic-gate * setenv() is a little more complex than putenv() because we have to allocate 34459f081edSraf * and construct an _environ entry on behalf of the caller. The bulk of the 3457c478bd9Sstevel@tonic-gate * work is still done in addtoenv(). 3467c478bd9Sstevel@tonic-gate */ 3477c478bd9Sstevel@tonic-gate 3487c478bd9Sstevel@tonic-gate int 3497c478bd9Sstevel@tonic-gate setenv(const char *envname, const char *envval, int overwrite) 3507c478bd9Sstevel@tonic-gate { 3517c478bd9Sstevel@tonic-gate chunk_t *new_chunk; 3527c478bd9Sstevel@tonic-gate char *new_string; 3537c478bd9Sstevel@tonic-gate size_t name_len; 3547c478bd9Sstevel@tonic-gate size_t val_len; 3557c478bd9Sstevel@tonic-gate int res; 3567c478bd9Sstevel@tonic-gate 3577c478bd9Sstevel@tonic-gate if (envname == NULL || *envname == 0 || strchr(envname, '=') != NULL) { 3587c478bd9Sstevel@tonic-gate errno = EINVAL; 3597c478bd9Sstevel@tonic-gate return (-1); 3607c478bd9Sstevel@tonic-gate } 3617c478bd9Sstevel@tonic-gate 3627c478bd9Sstevel@tonic-gate name_len = strlen(envname); 3637c478bd9Sstevel@tonic-gate val_len = strlen(envval); 3647c478bd9Sstevel@tonic-gate 3657c478bd9Sstevel@tonic-gate new_chunk = malloc(sizeof (chunk_t) + name_len + val_len + 2); 3667c478bd9Sstevel@tonic-gate if (new_chunk == NULL) { 3677c478bd9Sstevel@tonic-gate errno = ENOMEM; 3687c478bd9Sstevel@tonic-gate return (-1); 3697c478bd9Sstevel@tonic-gate } 3707c478bd9Sstevel@tonic-gate new_string = (char *)(new_chunk + 1); 3717c478bd9Sstevel@tonic-gate 3727c478bd9Sstevel@tonic-gate (void) memcpy(new_string, envname, name_len); 3737c478bd9Sstevel@tonic-gate new_string[name_len] = '='; 3747c478bd9Sstevel@tonic-gate (void) memcpy(new_string + name_len + 1, envval, val_len); 3757c478bd9Sstevel@tonic-gate new_string[name_len + 1 + val_len] = 0; 3767c478bd9Sstevel@tonic-gate 3777c478bd9Sstevel@tonic-gate if ((res = addtoenv(new_string, overwrite)) < 0) { 3787c478bd9Sstevel@tonic-gate free(new_chunk); 3797c478bd9Sstevel@tonic-gate if (res == -2) { 3807c478bd9Sstevel@tonic-gate /* The name already existed, but not an error. */ 3817c478bd9Sstevel@tonic-gate return (0); 3827c478bd9Sstevel@tonic-gate } else { 3837c478bd9Sstevel@tonic-gate /* i.e. res == -1 which means only one thing. */ 3847c478bd9Sstevel@tonic-gate errno = ENOMEM; 3857c478bd9Sstevel@tonic-gate return (-1); 3867c478bd9Sstevel@tonic-gate } 3877c478bd9Sstevel@tonic-gate } 3887c478bd9Sstevel@tonic-gate 3897c478bd9Sstevel@tonic-gate /* Hide potential leak of new_string. */ 3907c478bd9Sstevel@tonic-gate lmutex_lock(&update_lock); 3917c478bd9Sstevel@tonic-gate new_chunk->next = chunk_list; 3927c478bd9Sstevel@tonic-gate chunk_list = new_chunk; 3937c478bd9Sstevel@tonic-gate lmutex_unlock(&update_lock); 3947c478bd9Sstevel@tonic-gate 3957c478bd9Sstevel@tonic-gate return (0); 3967c478bd9Sstevel@tonic-gate } 3977c478bd9Sstevel@tonic-gate 3987c478bd9Sstevel@tonic-gate /* 39959f081edSraf * unsetenv() is tricky because we need to compress the _environ list in a way 4007c478bd9Sstevel@tonic-gate * which supports a lockless getenv(). The approach here is to move the first 4017c478bd9Sstevel@tonic-gate * entry from the enrivon list into the space occupied by the entry to be 40259f081edSraf * deleted, and then to increment _environ. This has the added advantage of 40359f081edSraf * making _any_ incremental linear search of the _environ list consistent (i.e. 4047c478bd9Sstevel@tonic-gate * we will not break any naughty apps which read the list without our help). 4057c478bd9Sstevel@tonic-gate */ 4067c478bd9Sstevel@tonic-gate int 4077c478bd9Sstevel@tonic-gate unsetenv(const char *name) 4087c478bd9Sstevel@tonic-gate { 4097c478bd9Sstevel@tonic-gate const char **p; 4107c478bd9Sstevel@tonic-gate char *value; 4117c478bd9Sstevel@tonic-gate 4127c478bd9Sstevel@tonic-gate if (name == NULL || *name == 0 || strchr(name, '=') != NULL) { 4137c478bd9Sstevel@tonic-gate errno = EINVAL; 4147c478bd9Sstevel@tonic-gate return (-1); 4157c478bd9Sstevel@tonic-gate } 4167c478bd9Sstevel@tonic-gate 417cb620785Sraf initenv(); 4187c478bd9Sstevel@tonic-gate 4197c478bd9Sstevel@tonic-gate lmutex_lock(&update_lock); 4207c478bd9Sstevel@tonic-gate 4217c478bd9Sstevel@tonic-gate /* 4227c478bd9Sstevel@tonic-gate * Find the target, overwrite it with the first entry, increment the 42359f081edSraf * _environ pointer. 4247c478bd9Sstevel@tonic-gate */ 4257c478bd9Sstevel@tonic-gate if ((p = findenv(my_environ, name, 1, &value)) != NULL) { 4267c478bd9Sstevel@tonic-gate /* Overwrite target with the first entry. */ 4277c478bd9Sstevel@tonic-gate *p = my_environ[0]; 4287c478bd9Sstevel@tonic-gate 4297c478bd9Sstevel@tonic-gate /* Ensure that the moved entry is visible to all. */ 4307c478bd9Sstevel@tonic-gate membar_producer(); 4317c478bd9Sstevel@tonic-gate 43259f081edSraf /* Shrink the _environ list. */ 4337c478bd9Sstevel@tonic-gate my_environ++; 43459f081edSraf _environ = my_environ; 4357c478bd9Sstevel@tonic-gate 4367c478bd9Sstevel@tonic-gate /* Make sure addtoenv() knows that we've removed a name. */ 4377c478bd9Sstevel@tonic-gate environ_gen++; 4387c478bd9Sstevel@tonic-gate } 4397c478bd9Sstevel@tonic-gate 4407c478bd9Sstevel@tonic-gate lmutex_unlock(&update_lock); 4417c478bd9Sstevel@tonic-gate return (0); 4427c478bd9Sstevel@tonic-gate } 4437c478bd9Sstevel@tonic-gate 444*23a1cceaSRoger A. Faulkner /* 445*23a1cceaSRoger A. Faulkner * Dump entire environment. 446*23a1cceaSRoger A. Faulkner */ 447*23a1cceaSRoger A. Faulkner int 448*23a1cceaSRoger A. Faulkner clearenv(void) 449*23a1cceaSRoger A. Faulkner { 450*23a1cceaSRoger A. Faulkner /* 451*23a1cceaSRoger A. Faulkner * Just drop the entire environment list on the floor, as it 452*23a1cceaSRoger A. Faulkner * would be non-trivial to try and free the used memory. 453*23a1cceaSRoger A. Faulkner */ 454*23a1cceaSRoger A. Faulkner static const char *nullp = NULL; 455*23a1cceaSRoger A. Faulkner 456*23a1cceaSRoger A. Faulkner lmutex_lock(&update_lock); 457*23a1cceaSRoger A. Faulkner _environ = &nullp; 458*23a1cceaSRoger A. Faulkner my_environ = NULL; 459*23a1cceaSRoger A. Faulkner environ_base = NULL; 460*23a1cceaSRoger A. Faulkner environ_size = 0; 461*23a1cceaSRoger A. Faulkner environ_gen++; 462*23a1cceaSRoger A. Faulkner membar_producer(); 463*23a1cceaSRoger A. Faulkner lmutex_unlock(&update_lock); 464*23a1cceaSRoger A. Faulkner 465*23a1cceaSRoger A. Faulkner return (0); 466*23a1cceaSRoger A. Faulkner } 467*23a1cceaSRoger A. Faulkner 4687c478bd9Sstevel@tonic-gate /* 4697c478bd9Sstevel@tonic-gate * At last, a lockless implementation of getenv()! 4707c478bd9Sstevel@tonic-gate */ 4717c478bd9Sstevel@tonic-gate char * 4727c478bd9Sstevel@tonic-gate getenv(const char *name) 4737c478bd9Sstevel@tonic-gate { 4747c478bd9Sstevel@tonic-gate char *value; 4757c478bd9Sstevel@tonic-gate 476cb620785Sraf initenv(); 4777c478bd9Sstevel@tonic-gate 47859f081edSraf if (findenv(_environ, name, 1, &value) != NULL) 4797c478bd9Sstevel@tonic-gate return (value); 4807c478bd9Sstevel@tonic-gate 4817c478bd9Sstevel@tonic-gate return (NULL); 4827c478bd9Sstevel@tonic-gate } 483