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