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 /* 237257d1b4Sraf * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */ 287c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 297c478bd9Sstevel@tonic-gate 307257d1b4Sraf #pragma ident "%Z%%M% %I% %E% SMI" 317c478bd9Sstevel@tonic-gate 327257d1b4Sraf #pragma weak _putenv = putenv 337c478bd9Sstevel@tonic-gate 347257d1b4Sraf #include "lint.h" 357c478bd9Sstevel@tonic-gate #include <mtlib.h> 367c478bd9Sstevel@tonic-gate #include <sys/types.h> 377c478bd9Sstevel@tonic-gate #include <thread.h> 387c478bd9Sstevel@tonic-gate #include <synch.h> 397c478bd9Sstevel@tonic-gate #include <stdlib.h> 407c478bd9Sstevel@tonic-gate #include <errno.h> 417c478bd9Sstevel@tonic-gate #include <string.h> 427c478bd9Sstevel@tonic-gate #include <atomic.h> 437c478bd9Sstevel@tonic-gate 447c478bd9Sstevel@tonic-gate #define MIN_ENV_SIZE 128 457c478bd9Sstevel@tonic-gate 46*59f081edSraf extern const char **_environ; 477c478bd9Sstevel@tonic-gate extern void clean_env(); 487c478bd9Sstevel@tonic-gate 497c478bd9Sstevel@tonic-gate /* 50*59f081edSraf * For performance and consistency reasons we expand the _environ list using 517c478bd9Sstevel@tonic-gate * the trusted "power of two, drop it on the floor" method. This allows for 527c478bd9Sstevel@tonic-gate * a lockless, single pass implementation of getenv(), yet the memory leak 537c478bd9Sstevel@tonic-gate * is bounded - in normal circumstances total wastage is never greater than 54*59f081edSraf * 3x the space needed to hold any _environ list. 557c478bd9Sstevel@tonic-gate * 56*59f081edSraf * The only abnormal circumstance is if an application modifies the _environ 577c478bd9Sstevel@tonic-gate * list pointer directly. Such an application does not conform to POSIX.1 587c478bd9Sstevel@tonic-gate * 2001. However, we also care about standards which did not foresee this 59*59f081edSraf * issue. For this reason we keep a working copy of our notion of _environ in 60*59f081edSraf * my_environ. If, when we are called upon to modify _environ, we ever detect 61*59f081edSraf * a mismatch between _environ and my_environ we discard all our assumptions 62*59f081edSraf * concerning the location and size of the _environ list. As an additional 63*59f081edSraf * precaution we only ever update _environ once we have finished manipulating 647c478bd9Sstevel@tonic-gate * our working copy. 657c478bd9Sstevel@tonic-gate * 667c478bd9Sstevel@tonic-gate * The setenv() API is inherently leaky but we are completely at the mercy 677c478bd9Sstevel@tonic-gate * of the application. 687c478bd9Sstevel@tonic-gate * 697c478bd9Sstevel@tonic-gate * To pacify leak detectors we chain all allocations which are at risk of 707c478bd9Sstevel@tonic-gate * being leaked in either of the above two scenarios. chunk_list must only 717c478bd9Sstevel@tonic-gate * be updated under the protection of update_lock. 727c478bd9Sstevel@tonic-gate * 73*59f081edSraf * Although we don't allocate the original _environ list it is likely that 747c478bd9Sstevel@tonic-gate * we will leak this too. Accordingly, we create a reference in initenv(). 757c478bd9Sstevel@tonic-gate * However, we can't be held responsible for such leaks in abnormal (see 767c478bd9Sstevel@tonic-gate * above) circumstances. 777c478bd9Sstevel@tonic-gate */ 787c478bd9Sstevel@tonic-gate 797c478bd9Sstevel@tonic-gate typedef struct chunk { 807c478bd9Sstevel@tonic-gate struct chunk *next; 817c478bd9Sstevel@tonic-gate } chunk_t; 827c478bd9Sstevel@tonic-gate 837c478bd9Sstevel@tonic-gate static mutex_t update_lock = DEFAULTMUTEX; 847c478bd9Sstevel@tonic-gate static const char **orig_environ = NULL; 857c478bd9Sstevel@tonic-gate static const char **my_environ = NULL; 867c478bd9Sstevel@tonic-gate static const char **environ_base = NULL; 877c478bd9Sstevel@tonic-gate static int environ_size = 0; 887c478bd9Sstevel@tonic-gate static int environ_gen = 0; 897c478bd9Sstevel@tonic-gate static int initenv_done = 0; 907c478bd9Sstevel@tonic-gate static chunk_t *chunk_list = NULL; 917c478bd9Sstevel@tonic-gate 927c478bd9Sstevel@tonic-gate /* 93*59f081edSraf * Compute the size an _environ list including the terminating NULL entry. 94*59f081edSraf * This is the only way we have to determine the size of an _environ list 957c478bd9Sstevel@tonic-gate * we didn't allocate. 967c478bd9Sstevel@tonic-gate */ 977c478bd9Sstevel@tonic-gate static int 987c478bd9Sstevel@tonic-gate envsize(const char **e) 997c478bd9Sstevel@tonic-gate { 1007c478bd9Sstevel@tonic-gate int size; 1017c478bd9Sstevel@tonic-gate 1027c478bd9Sstevel@tonic-gate if (e == NULL) 1037c478bd9Sstevel@tonic-gate return (0); 1047c478bd9Sstevel@tonic-gate 1057c478bd9Sstevel@tonic-gate for (size = 1; *e != NULL; e++) 1067c478bd9Sstevel@tonic-gate size++; 1077c478bd9Sstevel@tonic-gate 1087c478bd9Sstevel@tonic-gate return (size); 1097c478bd9Sstevel@tonic-gate } 1107c478bd9Sstevel@tonic-gate 1117c478bd9Sstevel@tonic-gate /* 1127c478bd9Sstevel@tonic-gate * Initialization for the following scenarios: 113*59f081edSraf * 1. The very first time we reference the _environ list we must call in the 114*59f081edSraf * NLSPATH janitor, make a reference to the original _environ list to keep 1157c478bd9Sstevel@tonic-gate * leak detectors happy, initialize my_environ and environ_base, and then 1167c478bd9Sstevel@tonic-gate * compute environ_size. 117*59f081edSraf * 2. Whenever we detect that someone else has hijacked _environ (something 1187c478bd9Sstevel@tonic-gate * very abnormal) we need to reinitialize my_environ and environ_base, 1197c478bd9Sstevel@tonic-gate * and then recompute environ_size. 1207c478bd9Sstevel@tonic-gate * 1217c478bd9Sstevel@tonic-gate * The local globals my_environ, environ_base and environ_size may be used 1227c478bd9Sstevel@tonic-gate * by others only if initenv_done is true and only under the protection of 1237c478bd9Sstevel@tonic-gate * update_lock. However, our callers, who must NOT be holding update_lock, 124*59f081edSraf * may safely test initenv_done or my_environ against _environ just prior to 1257c478bd9Sstevel@tonic-gate * calling us because we test these again whilst holding update_lock. 1267c478bd9Sstevel@tonic-gate */ 1277c478bd9Sstevel@tonic-gate static void 1287c478bd9Sstevel@tonic-gate initenv() 1297c478bd9Sstevel@tonic-gate { 130*59f081edSraf if ((my_environ != _environ) || !initenv_done) { 131cb620785Sraf lmutex_lock(&update_lock); 132*59f081edSraf if ((my_environ != _environ) || !initenv_done) { 133cb620785Sraf if (!initenv_done) { 134cb620785Sraf /* Call the NLSPATH janitor in. */ 135cb620785Sraf clean_env(); 1367c478bd9Sstevel@tonic-gate 137cb620785Sraf /* Pacify leak detectors in normal operation. */ 138*59f081edSraf orig_environ = _environ; 1397c478bd9Sstevel@tonic-gate #ifdef __lint 140cb620785Sraf my_environ = orig_environ; 1417c478bd9Sstevel@tonic-gate #endif 142cb620785Sraf } 1437c478bd9Sstevel@tonic-gate 144*59f081edSraf my_environ = _environ; 145cb620785Sraf environ_base = my_environ; 146cb620785Sraf environ_size = envsize(environ_base); 147cb620785Sraf membar_producer(); 1487c478bd9Sstevel@tonic-gate initenv_done = 1; 1497c478bd9Sstevel@tonic-gate } 150cb620785Sraf lmutex_unlock(&update_lock); 1517c478bd9Sstevel@tonic-gate } 152cb620785Sraf membar_consumer(); 1537c478bd9Sstevel@tonic-gate } 1547c478bd9Sstevel@tonic-gate 1557c478bd9Sstevel@tonic-gate /* 156*59f081edSraf * Search an _environ list for a particular entry. If name_only is set, then 1577c478bd9Sstevel@tonic-gate * string must be the entry name only, and we return the value of the first 1587c478bd9Sstevel@tonic-gate * match. Otherwise, string must be of the form "name=value", and we return 1597c478bd9Sstevel@tonic-gate * the address of the first matching entry. 1607c478bd9Sstevel@tonic-gate */ 1617c478bd9Sstevel@tonic-gate static const char ** 1627c478bd9Sstevel@tonic-gate findenv(const char **e, const char *string, int name_only, char **value) 1637c478bd9Sstevel@tonic-gate { 1647c478bd9Sstevel@tonic-gate char target; 1657c478bd9Sstevel@tonic-gate const char *s1; 1667c478bd9Sstevel@tonic-gate const char *s2; 1677c478bd9Sstevel@tonic-gate 1687c478bd9Sstevel@tonic-gate *value = NULL; 1697c478bd9Sstevel@tonic-gate 1707c478bd9Sstevel@tonic-gate if (e == NULL) 1717c478bd9Sstevel@tonic-gate return (NULL); 1727c478bd9Sstevel@tonic-gate 1737c478bd9Sstevel@tonic-gate target = name_only ? '\0' : '='; 1747c478bd9Sstevel@tonic-gate 1757c478bd9Sstevel@tonic-gate for (; (s2 = *e) != NULL; e++) { 1767c478bd9Sstevel@tonic-gate s1 = string; 1777c478bd9Sstevel@tonic-gate 1787c478bd9Sstevel@tonic-gate /* Fast comparison for first char. */ 1797c478bd9Sstevel@tonic-gate if (*s1 != *s2) 1807c478bd9Sstevel@tonic-gate continue; 1817c478bd9Sstevel@tonic-gate 1827c478bd9Sstevel@tonic-gate /* Slow comparison for rest of string. */ 1837c478bd9Sstevel@tonic-gate while (*s1 == *s2 && *s2 != '=') { 1847c478bd9Sstevel@tonic-gate s1++; 1857c478bd9Sstevel@tonic-gate s2++; 1867c478bd9Sstevel@tonic-gate } 1877c478bd9Sstevel@tonic-gate 1887c478bd9Sstevel@tonic-gate if (*s1 == target && *s2 == '=') { 1897c478bd9Sstevel@tonic-gate *value = (char *)s2 + 1; 1907c478bd9Sstevel@tonic-gate return (e); 1917c478bd9Sstevel@tonic-gate } 1927c478bd9Sstevel@tonic-gate } 1937c478bd9Sstevel@tonic-gate return (NULL); 1947c478bd9Sstevel@tonic-gate } 1957c478bd9Sstevel@tonic-gate 1967c478bd9Sstevel@tonic-gate /* 1977c478bd9Sstevel@tonic-gate * Common code for putenv() and setenv(). We support the lockless getenv() 1987c478bd9Sstevel@tonic-gate * by inserting new entries at the bottom of the list, and by growing the 1997c478bd9Sstevel@tonic-gate * list using the trusted "power of two, drop it on the floor" method. We 200*59f081edSraf * use a lock (update_lock) to protect all updates to the _environ list, but 2017c478bd9Sstevel@tonic-gate * we are obliged to release this lock whenever we call malloc() or free(). 2027c478bd9Sstevel@tonic-gate * A generation number (environ_gen) is bumped whenever names are added to, 203*59f081edSraf * or removed from, the _environ list so that we can detect collisions with 2047c478bd9Sstevel@tonic-gate * other updaters. 2057c478bd9Sstevel@tonic-gate * 2067c478bd9Sstevel@tonic-gate * Return values 2077c478bd9Sstevel@tonic-gate * 0 : success 2087c478bd9Sstevel@tonic-gate * -1 : with errno set 2097c478bd9Sstevel@tonic-gate * -2 : an entry already existed and overwrite was zero 2107c478bd9Sstevel@tonic-gate */ 2117c478bd9Sstevel@tonic-gate static int 2127c478bd9Sstevel@tonic-gate addtoenv(char *string, int overwrite) 2137c478bd9Sstevel@tonic-gate { 2147c478bd9Sstevel@tonic-gate char *value; 2157c478bd9Sstevel@tonic-gate const char **p; 2167c478bd9Sstevel@tonic-gate chunk_t *new_chunk; 2177c478bd9Sstevel@tonic-gate const char **new_environ; 2187c478bd9Sstevel@tonic-gate const char **new_base; 2197c478bd9Sstevel@tonic-gate int new_size; 2207c478bd9Sstevel@tonic-gate int old_gen; 2217c478bd9Sstevel@tonic-gate 222cb620785Sraf initenv(); 2237c478bd9Sstevel@tonic-gate 2247c478bd9Sstevel@tonic-gate lmutex_lock(&update_lock); 2257c478bd9Sstevel@tonic-gate 2267c478bd9Sstevel@tonic-gate for (;;) { 2277c478bd9Sstevel@tonic-gate /* 2287c478bd9Sstevel@tonic-gate * If the name already exists just overwrite the existing 2297c478bd9Sstevel@tonic-gate * entry -- except when we were called by setenv() without 2307c478bd9Sstevel@tonic-gate * the overwrite flag. 2317c478bd9Sstevel@tonic-gate */ 2327c478bd9Sstevel@tonic-gate if ((p = findenv(my_environ, string, 0, &value)) != NULL) { 2337c478bd9Sstevel@tonic-gate if (overwrite) { 2347c478bd9Sstevel@tonic-gate /* 2357c478bd9Sstevel@tonic-gate * Replace the value in situ. No name was 2367c478bd9Sstevel@tonic-gate * added, so there is no need to bump the 2377c478bd9Sstevel@tonic-gate * generation number. 2387c478bd9Sstevel@tonic-gate */ 2397c478bd9Sstevel@tonic-gate *p = string; 2407c478bd9Sstevel@tonic-gate lmutex_unlock(&update_lock); 2417c478bd9Sstevel@tonic-gate return (0); 2427c478bd9Sstevel@tonic-gate } else { 2437c478bd9Sstevel@tonic-gate /* No change. */ 2447c478bd9Sstevel@tonic-gate lmutex_unlock(&update_lock); 2457c478bd9Sstevel@tonic-gate return (-2); 2467c478bd9Sstevel@tonic-gate } 2477c478bd9Sstevel@tonic-gate } 2487c478bd9Sstevel@tonic-gate 2497c478bd9Sstevel@tonic-gate /* Try to insert the new entry at the bottom of the list. */ 2507c478bd9Sstevel@tonic-gate if (environ_base < my_environ) { 2517c478bd9Sstevel@tonic-gate /* 2527c478bd9Sstevel@tonic-gate * The new value must be visible before we decrement 253*59f081edSraf * the _environ list pointer. 2547c478bd9Sstevel@tonic-gate */ 2557c478bd9Sstevel@tonic-gate my_environ[-1] = string; 2567c478bd9Sstevel@tonic-gate membar_producer(); 2577c478bd9Sstevel@tonic-gate my_environ--; 258*59f081edSraf _environ = my_environ; 2597c478bd9Sstevel@tonic-gate 2607c478bd9Sstevel@tonic-gate /* 2617c478bd9Sstevel@tonic-gate * We've added a name, so bump the generation number. 2627c478bd9Sstevel@tonic-gate */ 2637c478bd9Sstevel@tonic-gate environ_gen++; 2647c478bd9Sstevel@tonic-gate 2657c478bd9Sstevel@tonic-gate lmutex_unlock(&update_lock); 2667c478bd9Sstevel@tonic-gate return (0); 2677c478bd9Sstevel@tonic-gate } 2687c478bd9Sstevel@tonic-gate 2697c478bd9Sstevel@tonic-gate /* 270*59f081edSraf * There is no room. Attempt to allocate a new _environ list 2717c478bd9Sstevel@tonic-gate * which is at least double the size of the current one. See 2727c478bd9Sstevel@tonic-gate * comment above concerning locking and malloc() etc. 2737c478bd9Sstevel@tonic-gate */ 2747c478bd9Sstevel@tonic-gate new_size = environ_size * 2; 2757c478bd9Sstevel@tonic-gate if (new_size < MIN_ENV_SIZE) 2767c478bd9Sstevel@tonic-gate new_size = MIN_ENV_SIZE; 2777c478bd9Sstevel@tonic-gate 2787c478bd9Sstevel@tonic-gate old_gen = environ_gen; 2797c478bd9Sstevel@tonic-gate lmutex_unlock(&update_lock); 2807c478bd9Sstevel@tonic-gate 2817c478bd9Sstevel@tonic-gate new_chunk = malloc(sizeof (chunk_t) + 2827c478bd9Sstevel@tonic-gate new_size * sizeof (char *)); 2837c478bd9Sstevel@tonic-gate if (new_chunk == NULL) { 2847c478bd9Sstevel@tonic-gate errno = ENOMEM; 2857c478bd9Sstevel@tonic-gate return (-1); 2867c478bd9Sstevel@tonic-gate } 2877c478bd9Sstevel@tonic-gate 2887c478bd9Sstevel@tonic-gate lmutex_lock(&update_lock); 2897c478bd9Sstevel@tonic-gate 2907c478bd9Sstevel@tonic-gate /* 2917c478bd9Sstevel@tonic-gate * If no other thread added or removed names while the lock 2927c478bd9Sstevel@tonic-gate * was dropped, it is time to break out of this loop. 2937c478bd9Sstevel@tonic-gate */ 2947c478bd9Sstevel@tonic-gate if (environ_gen == old_gen) 2957c478bd9Sstevel@tonic-gate break; 2967c478bd9Sstevel@tonic-gate 2977c478bd9Sstevel@tonic-gate /* 2987c478bd9Sstevel@tonic-gate * At least one name has been added or removed, so we need to 2997c478bd9Sstevel@tonic-gate * try again. It is very likely that we will find sufficient 3007c478bd9Sstevel@tonic-gate * space the next time around. 3017c478bd9Sstevel@tonic-gate */ 3027c478bd9Sstevel@tonic-gate lmutex_unlock(&update_lock); 3037c478bd9Sstevel@tonic-gate free(new_chunk); 3047c478bd9Sstevel@tonic-gate lmutex_lock(&update_lock); 3057c478bd9Sstevel@tonic-gate } 3067c478bd9Sstevel@tonic-gate 3077c478bd9Sstevel@tonic-gate /* Add the new chunk to chunk_list to hide potential future leak. */ 3087c478bd9Sstevel@tonic-gate new_chunk->next = chunk_list; 3097c478bd9Sstevel@tonic-gate chunk_list = new_chunk; 3107c478bd9Sstevel@tonic-gate 311*59f081edSraf /* Copy the old _environ list into the top of the new _environ list. */ 3127c478bd9Sstevel@tonic-gate new_base = (const char **)(new_chunk + 1); 3137c478bd9Sstevel@tonic-gate new_environ = &new_base[(new_size - 1) - environ_size]; 3147c478bd9Sstevel@tonic-gate (void) memcpy(new_environ, my_environ, environ_size * sizeof (char *)); 3157c478bd9Sstevel@tonic-gate 316*59f081edSraf /* Insert the new entry at the bottom of the new _environ list. */ 3177c478bd9Sstevel@tonic-gate new_environ[-1] = string; 3187c478bd9Sstevel@tonic-gate new_environ--; 3197c478bd9Sstevel@tonic-gate 320*59f081edSraf /* Ensure that the new _environ list is visible to all. */ 3217c478bd9Sstevel@tonic-gate membar_producer(); 3227c478bd9Sstevel@tonic-gate 323*59f081edSraf /* Make the switch (dropping the old _environ list on the floor). */ 3247c478bd9Sstevel@tonic-gate environ_base = new_base; 3257c478bd9Sstevel@tonic-gate my_environ = new_environ; 326*59f081edSraf _environ = my_environ; 3277c478bd9Sstevel@tonic-gate environ_size = new_size; 3287c478bd9Sstevel@tonic-gate 3297c478bd9Sstevel@tonic-gate /* We've added a name, so bump the generation number. */ 3307c478bd9Sstevel@tonic-gate environ_gen++; 3317c478bd9Sstevel@tonic-gate 3327c478bd9Sstevel@tonic-gate lmutex_unlock(&update_lock); 3337c478bd9Sstevel@tonic-gate return (0); 3347c478bd9Sstevel@tonic-gate } 3357c478bd9Sstevel@tonic-gate 3367c478bd9Sstevel@tonic-gate /* 3377c478bd9Sstevel@tonic-gate * All the work for putenv() is done in addtoenv(). 3387c478bd9Sstevel@tonic-gate */ 3397c478bd9Sstevel@tonic-gate int 3407c478bd9Sstevel@tonic-gate putenv(char *string) 3417c478bd9Sstevel@tonic-gate { 3427c478bd9Sstevel@tonic-gate return (addtoenv(string, 1)); 3437c478bd9Sstevel@tonic-gate } 3447c478bd9Sstevel@tonic-gate 3457c478bd9Sstevel@tonic-gate /* 3467c478bd9Sstevel@tonic-gate * setenv() is a little more complex than putenv() because we have to allocate 347*59f081edSraf * and construct an _environ entry on behalf of the caller. The bulk of the 3487c478bd9Sstevel@tonic-gate * work is still done in addtoenv(). 3497c478bd9Sstevel@tonic-gate */ 3507c478bd9Sstevel@tonic-gate 3517c478bd9Sstevel@tonic-gate int 3527c478bd9Sstevel@tonic-gate setenv(const char *envname, const char *envval, int overwrite) 3537c478bd9Sstevel@tonic-gate { 3547c478bd9Sstevel@tonic-gate chunk_t *new_chunk; 3557c478bd9Sstevel@tonic-gate char *new_string; 3567c478bd9Sstevel@tonic-gate size_t name_len; 3577c478bd9Sstevel@tonic-gate size_t val_len; 3587c478bd9Sstevel@tonic-gate int res; 3597c478bd9Sstevel@tonic-gate 3607c478bd9Sstevel@tonic-gate if (envname == NULL || *envname == 0 || strchr(envname, '=') != NULL) { 3617c478bd9Sstevel@tonic-gate errno = EINVAL; 3627c478bd9Sstevel@tonic-gate return (-1); 3637c478bd9Sstevel@tonic-gate } 3647c478bd9Sstevel@tonic-gate 3657c478bd9Sstevel@tonic-gate name_len = strlen(envname); 3667c478bd9Sstevel@tonic-gate val_len = strlen(envval); 3677c478bd9Sstevel@tonic-gate 3687c478bd9Sstevel@tonic-gate new_chunk = malloc(sizeof (chunk_t) + name_len + val_len + 2); 3697c478bd9Sstevel@tonic-gate if (new_chunk == NULL) { 3707c478bd9Sstevel@tonic-gate errno = ENOMEM; 3717c478bd9Sstevel@tonic-gate return (-1); 3727c478bd9Sstevel@tonic-gate } 3737c478bd9Sstevel@tonic-gate new_string = (char *)(new_chunk + 1); 3747c478bd9Sstevel@tonic-gate 3757c478bd9Sstevel@tonic-gate (void) memcpy(new_string, envname, name_len); 3767c478bd9Sstevel@tonic-gate new_string[name_len] = '='; 3777c478bd9Sstevel@tonic-gate (void) memcpy(new_string + name_len + 1, envval, val_len); 3787c478bd9Sstevel@tonic-gate new_string[name_len + 1 + val_len] = 0; 3797c478bd9Sstevel@tonic-gate 3807c478bd9Sstevel@tonic-gate if ((res = addtoenv(new_string, overwrite)) < 0) { 3817c478bd9Sstevel@tonic-gate free(new_chunk); 3827c478bd9Sstevel@tonic-gate if (res == -2) { 3837c478bd9Sstevel@tonic-gate /* The name already existed, but not an error. */ 3847c478bd9Sstevel@tonic-gate return (0); 3857c478bd9Sstevel@tonic-gate } else { 3867c478bd9Sstevel@tonic-gate /* i.e. res == -1 which means only one thing. */ 3877c478bd9Sstevel@tonic-gate errno = ENOMEM; 3887c478bd9Sstevel@tonic-gate return (-1); 3897c478bd9Sstevel@tonic-gate } 3907c478bd9Sstevel@tonic-gate } 3917c478bd9Sstevel@tonic-gate 3927c478bd9Sstevel@tonic-gate /* Hide potential leak of new_string. */ 3937c478bd9Sstevel@tonic-gate lmutex_lock(&update_lock); 3947c478bd9Sstevel@tonic-gate new_chunk->next = chunk_list; 3957c478bd9Sstevel@tonic-gate chunk_list = new_chunk; 3967c478bd9Sstevel@tonic-gate lmutex_unlock(&update_lock); 3977c478bd9Sstevel@tonic-gate 3987c478bd9Sstevel@tonic-gate return (0); 3997c478bd9Sstevel@tonic-gate } 4007c478bd9Sstevel@tonic-gate 4017c478bd9Sstevel@tonic-gate /* 402*59f081edSraf * unsetenv() is tricky because we need to compress the _environ list in a way 4037c478bd9Sstevel@tonic-gate * which supports a lockless getenv(). The approach here is to move the first 4047c478bd9Sstevel@tonic-gate * entry from the enrivon list into the space occupied by the entry to be 405*59f081edSraf * deleted, and then to increment _environ. This has the added advantage of 406*59f081edSraf * making _any_ incremental linear search of the _environ list consistent (i.e. 4077c478bd9Sstevel@tonic-gate * we will not break any naughty apps which read the list without our help). 4087c478bd9Sstevel@tonic-gate */ 4097c478bd9Sstevel@tonic-gate int 4107c478bd9Sstevel@tonic-gate unsetenv(const char *name) 4117c478bd9Sstevel@tonic-gate { 4127c478bd9Sstevel@tonic-gate const char **p; 4137c478bd9Sstevel@tonic-gate char *value; 4147c478bd9Sstevel@tonic-gate 4157c478bd9Sstevel@tonic-gate if (name == NULL || *name == 0 || strchr(name, '=') != NULL) { 4167c478bd9Sstevel@tonic-gate errno = EINVAL; 4177c478bd9Sstevel@tonic-gate return (-1); 4187c478bd9Sstevel@tonic-gate } 4197c478bd9Sstevel@tonic-gate 420cb620785Sraf initenv(); 4217c478bd9Sstevel@tonic-gate 4227c478bd9Sstevel@tonic-gate lmutex_lock(&update_lock); 4237c478bd9Sstevel@tonic-gate 4247c478bd9Sstevel@tonic-gate /* 4257c478bd9Sstevel@tonic-gate * Find the target, overwrite it with the first entry, increment the 426*59f081edSraf * _environ pointer. 4277c478bd9Sstevel@tonic-gate */ 4287c478bd9Sstevel@tonic-gate if ((p = findenv(my_environ, name, 1, &value)) != NULL) { 4297c478bd9Sstevel@tonic-gate /* Overwrite target with the first entry. */ 4307c478bd9Sstevel@tonic-gate *p = my_environ[0]; 4317c478bd9Sstevel@tonic-gate 4327c478bd9Sstevel@tonic-gate /* Ensure that the moved entry is visible to all. */ 4337c478bd9Sstevel@tonic-gate membar_producer(); 4347c478bd9Sstevel@tonic-gate 435*59f081edSraf /* Shrink the _environ list. */ 4367c478bd9Sstevel@tonic-gate my_environ++; 437*59f081edSraf _environ = my_environ; 4387c478bd9Sstevel@tonic-gate 4397c478bd9Sstevel@tonic-gate /* Make sure addtoenv() knows that we've removed a name. */ 4407c478bd9Sstevel@tonic-gate environ_gen++; 4417c478bd9Sstevel@tonic-gate } 4427c478bd9Sstevel@tonic-gate 4437c478bd9Sstevel@tonic-gate lmutex_unlock(&update_lock); 4447c478bd9Sstevel@tonic-gate return (0); 4457c478bd9Sstevel@tonic-gate } 4467c478bd9Sstevel@tonic-gate 4477c478bd9Sstevel@tonic-gate /* 4487c478bd9Sstevel@tonic-gate * At last, a lockless implementation of getenv()! 4497c478bd9Sstevel@tonic-gate */ 4507c478bd9Sstevel@tonic-gate char * 4517c478bd9Sstevel@tonic-gate getenv(const char *name) 4527c478bd9Sstevel@tonic-gate { 4537c478bd9Sstevel@tonic-gate char *value; 4547c478bd9Sstevel@tonic-gate 455cb620785Sraf initenv(); 4567c478bd9Sstevel@tonic-gate 457*59f081edSraf if (findenv(_environ, name, 1, &value) != NULL) 4587c478bd9Sstevel@tonic-gate return (value); 4597c478bd9Sstevel@tonic-gate 4607c478bd9Sstevel@tonic-gate return (NULL); 4617c478bd9Sstevel@tonic-gate } 462