1/* 2 * Copyright (c) 1987, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#if defined(LIBC_SCCS) && !defined(lint) 31static char sccsid[] = "@(#)setenv.c 8.1 (Berkeley) 6/4/93"; 32#endif /* LIBC_SCCS and not lint */ 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD: src/lib/libc/stdlib/setenv.c,v 1.14 2007/05/01 16:02:41 ache Exp $"); 35 36#include <stddef.h> 37#include <stdlib.h> 38#include <string.h> 39#include <crt_externs.h> 40#include <errno.h> 41#include <sys/types.h> 42#include <fcntl.h> 43 44struct owned_ptr; 45__private_extern__ char *__findenv(const char *, int *, char **); 46__private_extern__ int __setenv(const char *, const char *, int, int, char ***, struct owned_ptr *); 47__private_extern__ void __unsetenv(const char *, char **, struct owned_ptr *); 48 49__private_extern__ struct owned_ptr *__env_owned; 50__private_extern__ int __init__env_owned(int); 51 52/* 53 * _cthread_init_routine used to be called from crt1.o to initialize threads. 54 * This is no longer needed, as initialization happens in dylib initializers, 55 * but is provided to maintain backwards compatibility. Normally, for 10.5 56 * or greater, _cthread_init_routine does nothing. 57 * 58 * Before 10.5, the _start routine in crt1.o clobbers environ with the original 59 * stack value, which effectively undoes any environment changes made in 60 * initializers. When LEGACY_CRT1_ENVIRON is defined, we replace the 61 * do-nothing routine with one that attempts to restore the environ value. 62 * But this only works if the setenv (and family) routines were used 63 * exclusively, (no direct manipulation of environ). Note that according to 64 * SUSv3, direct manipulation of environ may result in undefined behavior in 65 * setenv and family, so we don't support that (on less than 10.5). 66 */ 67#ifdef BUILDING_VARIANT 68# ifdef LEGACY_CRT1_ENVIRON 69extern char **_saved_environ; 70# endif /* LEGACY_CRT1_ENVIRON */ 71#else /* !BUILDING_VARIANT */ 72# ifdef LEGACY_CRT1_ENVIRON 73__private_extern__ char **_saved_environ = NULL; 74 75static int 76_legacy_crt1_environ(void) 77{ 78 if (_saved_environ) *_NSGetEnviron() = _saved_environ; 79 return 0; 80} 81int (*_cthread_init_routine)(void) = _legacy_crt1_environ; 82 83# else /* !LEGACY_CRT1_ENVIRON */ 84static int _do_nothing(void) { return 0; } 85int (*_cthread_init_routine)(void) = _do_nothing; 86# endif /* !LEGACY_CRT1_ENVIRON */ 87 88__private_extern__ struct owned_ptr *__env_owned = NULL; 89 90/* 91 * The owned_ptr structure is a table of pointers that we own, and can 92 * realloc/free as we choose. table[0] is always NULL, so it is always 93 * less that any real pointer. "used" is the number of table entries in use 94 * (including the initial NULL), while "size" is the allocated size of the 95 * table (used to enlarge the size of the table when needed). 96 */ 97struct owned_ptr { 98 const void **table; 99 int used; 100 int size; 101}; 102 103#define OWNED_PTR_INITIAL_SIZE 8 104 105__private_extern__ int _owned_ptr_search(struct owned_ptr * __restrict, const void * __restrict, int * __restrict); 106 107__private_extern__ void 108_owned_ptr_add(struct owned_ptr * __restrict owned, const void * __restrict ptr) 109{ 110 int index; 111 112 if (_owned_ptr_search(owned, ptr, &index) == 0) return; /* already there */ 113 if (owned->used >= owned->size) { 114 int new_size = 2 * owned->size; 115 const void **new_table = (const void **)realloc(owned->table, 116 new_size * sizeof(const void *)); 117 if (!new_table) { 118 /* no memory to enlarge the table, so just drop */ 119 return; 120 } 121 owned->table = new_table; 122 owned->size = new_size; 123 } 124 memmove(owned->table + index + 2, owned->table + index + 1, 125 sizeof(void *) * (owned->used - index - 1)); 126 owned->table[index + 1] = ptr; 127 owned->used++; 128} 129 130__private_extern__ struct owned_ptr * 131_owned_ptr_alloc(void) 132{ 133 struct owned_ptr *owned; 134 135 owned = (struct owned_ptr *)malloc(sizeof(struct owned_ptr)); 136 if (!owned) return NULL; 137 owned->table = (const void **)malloc(OWNED_PTR_INITIAL_SIZE * 138 sizeof(const void *)); 139 if (!owned->table) { 140 int save = errno; 141 free(owned); 142 errno = save; 143 return NULL; 144 } 145 owned->table[0] = NULL; 146 owned->used = 1; 147 owned->size = OWNED_PTR_INITIAL_SIZE; 148 return owned; 149} 150 151__private_extern__ void 152_owned_ptr_delete(struct owned_ptr *owned, int index) 153{ 154 if (!index || index >= owned->used) return; 155 memmove(owned->table + index, owned->table + index + 1, 156 sizeof(void *) * (owned->used - index - 1)); 157 owned->used--; 158} 159 160__private_extern__ void 161_owned_ptr_free(struct owned_ptr *owned) 162{ 163 free(owned->table); 164 free(owned); 165} 166 167/* 168 * Search owned->table for "ptr". Zero is returned if found, non-zero means 169 * not found. If "result" is non-NULL, the found index is returned in it, or 170 * if not found, the index of the immediate lower value (ptr can be inserted 171 * after this index). 172 */ 173__private_extern__ int 174_owned_ptr_search(struct owned_ptr * __restrict owned, const void * __restrict ptr, int * __restrict result) 175{ 176 int low = 0; 177 int high = owned->used - 1; 178 int cur; 179 180 if (owned->table[high] < ptr) { 181 if (result) *result = high; 182 return -1; 183 } else if (owned->table[high] == ptr) { 184 if (result) *result = high; 185 return 0; 186 } 187 while (high - low > 1) { 188 cur = (low + high) / 2; 189 if (ptr > owned->table[cur]) { 190 low = cur; 191 } else if (ptr < owned->table[cur]) { 192 high = cur; 193 } else { 194 /* match found */ 195 if (result) *result = cur; 196 return 0; 197 } 198 } 199 /* no match found; *result will be the insert-after position */ 200 if (result) *result = low; 201 return -1; 202} 203 204/* 205 * Initialize the process's __env_owned structure 206 */ 207__private_extern__ int 208__init__env_owned(int should_set_errno) 209{ 210 int save; 211 212 if (__env_owned) return 0; 213 214 if (!should_set_errno) 215 save = errno; 216 __env_owned = _owned_ptr_alloc(); 217 if (!__env_owned) { 218 if (!should_set_errno) 219 errno = save; 220 return -1; 221 } 222 return 0; 223} 224 225/* 226 * The copy flag may have 3 values: 227 * 1 - make a copy of the name/value pair 228 * 0 - take the name as a user-supplied name=value string 229 * -1 - like 0, except we copy of the name=value string in name 230 */ 231__private_extern__ int 232__setenv(name, value, rewrite, copy, environp, owned) 233 const char *name; 234 const char *value; 235 int rewrite, copy; 236 char ***environp; 237 struct owned_ptr *owned; 238{ 239 char *c; 240 int offset; 241 int oindex; 242 243 if ((c = __findenv(name, &offset, *environp))) { /* find if already exists */ 244 char *e; 245 if (!rewrite) 246 return (0); 247 /* 248 * In UNIX03, we can overwrite only if we allocated the 249 * string. Then we can realloc it if it is too small. 250 */ 251 e = (*environp)[offset]; 252 if (_owned_ptr_search(owned, e, &oindex) == 0) { 253 if (copy > 0) { 254 size_t l_value = strlen(value); 255 if (strlen(c) < l_value) { /* old smaller; resize*/ 256 char *r; 257 size_t len = c - e; 258 if ((r = realloc(e, l_value + len + 1)) == NULL) 259 return (-1); 260 if (r != e) { 261 (*environp)[offset] = r; 262 c = r + len; 263 _owned_ptr_delete(owned, oindex); 264 _owned_ptr_add(owned, r); 265 } 266 } 267 while ( (*c++ = *value++) ); 268 return (0); 269 } 270 /* Free up the slot for later use (putenv) */ 271 _owned_ptr_delete(owned, oindex); 272 free(e); 273 } 274 } else { /* create new slot */ 275 int cnt; 276 char **p; 277 278 for (p = *environp, cnt = 0; *p; ++p, ++cnt); 279 if (_owned_ptr_search(owned, *environp, &oindex) == 0) { /* just increase size */ 280 p = (char **)realloc((char *)*environp, 281 (size_t)(sizeof(char *) * (cnt + 2))); 282 if (!p) 283 return (-1); 284 if (*environp != p) { 285 _owned_ptr_delete(owned, oindex); 286 _owned_ptr_add(owned, p); 287 *environp = p; 288 } 289 } 290 else { /* get new space */ 291 /* copy old entries into it */ 292 p = malloc((size_t)(sizeof(char *) * (cnt + 2))); 293 if (!p) 294 return (-1); 295 _owned_ptr_add(owned, p); 296 bcopy(*environp, p, cnt * sizeof(char *)); 297 *environp = p; 298 } 299 (*environp)[cnt + 1] = NULL; 300 offset = cnt; 301 } 302 /* For non Unix03, or UnixO3 setenv(), we make a copy of the user's 303 * strings. For Unix03 putenv(), we put the string directly in 304 * the environment. */ 305 if (copy > 0) { 306 for (c = (char *)name; *c && *c != '='; ++c); /* no `=' in name */ 307 if (!((*environp)[offset] = /* name + `=' + value */ 308 malloc((size_t)((int)(c - name) + strlen(value) + 2)))) 309 return (-1); 310 _owned_ptr_add(owned, (*environp)[offset]); 311 for (c = (*environp)[offset]; (*c = *name++) && *c != '='; ++c); 312 for (*c++ = '='; (*c++ = *value++); ); 313 } else { 314 /* the legacy behavior copies the string */ 315 if (copy < 0) { 316 size_t len = strlen(name); 317 if((c = malloc(len + 1)) == NULL) 318 return (-1); 319 _owned_ptr_add(owned, c); 320 memcpy(c, name, len + 1); 321 name = c; 322 } 323 (*environp)[offset] = (char *)name; 324 } 325 return (0); 326} 327 328__private_extern__ void 329__unsetenv(const char *name, char **environ, struct owned_ptr *owned) 330{ 331 char **p; 332 int offset; 333 int oindex; 334 335 while (__findenv(name, &offset, environ)) { /* if set multiple times */ 336 /* if we malloc-ed it, free it first */ 337 if (_owned_ptr_search(owned, environ[offset], &oindex) == 0) { 338 _owned_ptr_delete(owned, oindex); 339 free(environ[offset]); 340 } 341 for (p = &environ[offset];; ++p) 342 if (!(*p = *(p + 1))) 343 break; 344 } 345} 346 347/****************************************************************************/ 348/* 349 * _allocenvstate -- SPI that creates a new state (opaque) 350 */ 351void * 352_allocenvstate(void) 353{ 354 return _owned_ptr_alloc(); 355} 356 357/* 358 * _copyenv -- SPI that copies a NULL-tereminated char * array in a newly 359 * allocated buffer, compatible with the other SPI env routines. If env 360 * is NULL, a char * array composed of a single NULL is returned. NULL 361 * is returned on error. (This isn't needed anymore, as __setenv will 362 * automatically make a copy.) 363 */ 364char ** 365_copyenv(char **env) 366{ 367 char **p; 368 int cnt = 1; 369 370 if (env) 371 for (p = env; *p; ++p, ++cnt); 372 p = (char **)malloc((size_t)(sizeof(char *) * cnt)); 373 if (!p) 374 return (NULL); 375 if (env) 376 bcopy(env, p, cnt * sizeof(char *)); 377 else 378 *p = NULL; 379 return p; 380} 381 382/* 383 * _deallocenvstate -- SPI that frees all the memory associated with the state 384 * and all allocated strings, including the environment array itself if it 385 * was copied. 386 */ 387int 388_deallocenvstate(void *state) 389{ 390 struct owned_ptr *owned; 391 392 if (!(owned = (struct owned_ptr *)state) || owned == __env_owned) { 393 errno = EINVAL; 394 return -1; 395 } 396 _owned_ptr_free(owned); 397 return 0; 398} 399 400/* 401 * setenvp -- SPI using an arbitrary pointer to string array and an env state, 402 * created by _allocenvstate(). Initial checking is not done. 403 * 404 * Set the value of the environmental variable "name" to be 405 * "value". If rewrite is set, replace any current value. 406 */ 407int 408_setenvp(const char *name, const char *value, int rewrite, char ***envp, void *state) 409{ 410 if (__init__env_owned(1)) return (-1); 411 return (__setenv(name, value, rewrite, 1, envp, (state ? (struct owned_ptr *)state : __env_owned))); 412} 413 414/* 415 * unsetenv(name) -- SPI using an arbitrary pointer to string array and an env 416 * state, created by _allocenvstate(). Initial checking is not done. 417 * 418 * Delete environmental variable "name". 419 */ 420int 421_unsetenvp(const char *name, char ***envp, void *state) 422{ 423 if (__init__env_owned(1)) return (-1); 424 __unsetenv(name, *envp, (state ? (struct owned_ptr *)state : __env_owned)); 425 return 0; 426} 427 428#endif /* !BUILD_VARIANT */ 429 430/* 431 * setenv -- 432 * Set the value of the environmental variable "name" to be 433 * "value". If rewrite is set, replace any current value. 434 */ 435int 436setenv(name, value, rewrite) 437 const char *name; 438 const char *value; 439 int rewrite; 440{ 441#ifdef LEGACY_CRT1_ENVIRON 442 int ret; 443#endif /* LEGACY_CRT1_ENVIRON */ 444 445 /* no null ptr or empty str */ 446 if(name == NULL || *name == 0) { 447 errno = EINVAL; 448 return (-1); 449 } 450 451#if __DARWIN_UNIX03 452 /* no '=' in name */ 453 if (strchr(name, '=')) { 454 errno = EINVAL; 455 return (-1); 456 } 457#endif /* __DARWIN_UNIX03 */ 458 459 if (*value == '=') /* no `=' in value */ 460 ++value; 461 if (__init__env_owned(1)) return (-1); 462#ifdef LEGACY_CRT1_ENVIRON 463 ret = __setenv(name, value, rewrite, 1, _NSGetEnviron(), __env_owned); 464 _saved_environ = *_NSGetEnviron(); 465 return ret; 466#else /* !LEGACY_CRT1_ENVIRON */ 467 return (__setenv(name, value, rewrite, 1, _NSGetEnviron(), __env_owned)); 468#endif /* !LEGACY_CRT1_ENVIRON */ 469} 470 471/* 472 * unsetenv(name) -- 473 * Delete environmental variable "name". 474 */ 475#if __DARWIN_UNIX03 476int 477#else /* !__DARWIN_UNIX03 */ 478void 479#endif /* __DARWIN_UNIX03 */ 480unsetenv(name) 481 const char *name; 482{ 483#if __DARWIN_UNIX03 484 /* no null ptr or empty str */ 485 if(name == NULL || *name == 0) { 486 errno = EINVAL; 487 return (-1); 488 } 489 490 /* no '=' in name */ 491 if (strchr(name, '=')) { 492 errno = EINVAL; 493 return (-1); 494 } 495 if (__init__env_owned(1)) return (-1); 496#else /* !__DARWIN_UNIX03 */ 497 /* no null ptr or empty str */ 498 if(name == NULL || *name == 0) 499 return; 500 if (__init__env_owned(0)) return; 501#endif /* __DARWIN_UNIX03 */ 502 __unsetenv(name, *_NSGetEnviron(), __env_owned); 503#if __DARWIN_UNIX03 504 return 0; 505#endif /* __DARWIN_UNIX03 */ 506} 507