setenv.c
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) 31 static 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 44 struct owned_ptr; 45 extern char *__findenv_locked(const char *, int *, char **); 46 extern int __setenv_locked(const char *, const char *, int, int, char ***, struct owned_ptr *); 47 extern void __unsetenv_locked(const char *, char **, struct owned_ptr *); 48 49 extern void __environ_lock(void); 50 extern void __environ_unlock(void); 51 52 extern struct owned_ptr *__env_owned; 53 extern int __init__env_owned_locked(int); 54 55 /* 56 * _cthread_init_routine used to be called from crt1.o to initialize threads. 57 * This is no longer needed, as initialization happens in dylib initializers, 58 * but is provided to maintain backwards compatibility. Normally, for 10.5 59 * or greater, _cthread_init_routine does nothing. 60 * 61 * Before 10.5, the _start routine in crt1.o clobbers environ with the original 62 * stack value, which effectively undoes any environment changes made in 63 * initializers. When LEGACY_CRT1_ENVIRON is defined, we replace the 64 * do-nothing routine with one that attempts to restore the environ value. 65 * But this only works if the setenv (and family) routines were used 66 * exclusively, (no direct manipulation of environ). Note that according to 67 * SUSv3, direct manipulation of environ may result in undefined behavior in 68 * setenv and family, so we don't support that (on less than 10.5). 69 */ 70 #ifdef BUILDING_VARIANT 71 # ifdef LEGACY_CRT1_ENVIRON 72 extern char **_saved_environ; 73 # endif /* LEGACY_CRT1_ENVIRON */ 74 #else /* !BUILDING_VARIANT */ 75 # ifdef LEGACY_CRT1_ENVIRON 76 __private_extern__ char **_saved_environ = NULL; 77 78 static int 79 _legacy_crt1_environ(void) 80 { 81 if (_saved_environ) *_NSGetEnviron() = _saved_environ; 82 return 0; 83 } 84 int (*_cthread_init_routine)(void) = _legacy_crt1_environ; 85 86 # else /* !LEGACY_CRT1_ENVIRON */ 87 static int _do_nothing(void) { return 0; } 88 int (*_cthread_init_routine)(void) = _do_nothing; 89 # endif /* !LEGACY_CRT1_ENVIRON */ 90 91 __private_extern__ struct owned_ptr *__env_owned = NULL; 92 93 /* 94 * The owned_ptr structure is a table of pointers that we own, and can 95 * realloc/free as we choose. table[0] is always NULL, so it is always 96 * less that any real pointer. "used" is the number of table entries in use 97 * (including the initial NULL), while "size" is the allocated size of the 98 * table (used to enlarge the size of the table when needed). 99 */ 100 struct owned_ptr { 101 const void **table; 102 int used; 103 int size; 104 }; 105 106 #define OWNED_PTR_INITIAL_SIZE 8 107 108 __private_extern__ int _owned_ptr_search(struct owned_ptr * __restrict, const void * __restrict, int * __restrict); 109 110 __private_extern__ void 111 _owned_ptr_add(struct owned_ptr * __restrict owned, const void * __restrict ptr) 112 { 113 int index; 114 115 if (_owned_ptr_search(owned, ptr, &index) == 0) return; /* already there */ 116 if (owned->used >= owned->size) { 117 int new_size = 2 * owned->size; 118 const void **new_table = (const void **)realloc(owned->table, 119 new_size * sizeof(const void *)); 120 if (!new_table) { 121 /* no memory to enlarge the table, so just drop */ 122 return; 123 } 124 owned->table = new_table; 125 owned->size = new_size; 126 } 127 memmove(owned->table + index + 2, owned->table + index + 1, 128 sizeof(void *) * (owned->used - index - 1)); 129 owned->table[index + 1] = ptr; 130 owned->used++; 131 } 132 133 __private_extern__ struct owned_ptr * 134 _owned_ptr_alloc(void) 135 { 136 struct owned_ptr *owned; 137 138 owned = (struct owned_ptr *)malloc(sizeof(struct owned_ptr)); 139 if (!owned) return NULL; 140 owned->table = (const void **)malloc(OWNED_PTR_INITIAL_SIZE * 141 sizeof(const void *)); 142 if (!owned->table) { 143 int save = errno; 144 free(owned); 145 errno = save; 146 return NULL; 147 } 148 owned->table[0] = NULL; 149 owned->used = 1; 150 owned->size = OWNED_PTR_INITIAL_SIZE; 151 return owned; 152 } 153 154 __private_extern__ void 155 _owned_ptr_delete(struct owned_ptr *owned, int index) 156 { 157 if (!index || index >= owned->used) return; 158 memmove(owned->table + index, owned->table + index + 1, 159 sizeof(void *) * (owned->used - index - 1)); 160 owned->used--; 161 } 162 163 __private_extern__ void 164 _owned_ptr_free(struct owned_ptr *owned) 165 { 166 free(owned->table); 167 free(owned); 168 } 169 170 /* 171 * Search owned->table for "ptr". Zero is returned if found, non-zero means 172 * not found. If "result" is non-NULL, the found index is returned in it, or 173 * if not found, the index of the immediate lower value (ptr can be inserted 174 * after this index). 175 */ 176 __private_extern__ int 177 _owned_ptr_search(struct owned_ptr * __restrict owned, const void * __restrict ptr, int * __restrict result) 178 { 179 int low = 0; 180 int high = owned->used - 1; 181 int cur; 182 183 if (owned->table[high] < ptr) { 184 if (result) *result = high; 185 return -1; 186 } else if (owned->table[high] == ptr) { 187 if (result) *result = high; 188 return 0; 189 } 190 while (high - low > 1) { 191 cur = (low + high) / 2; 192 if (ptr > owned->table[cur]) { 193 low = cur; 194 } else if (ptr < owned->table[cur]) { 195 high = cur; 196 } else { 197 /* match found */ 198 if (result) *result = cur; 199 return 0; 200 } 201 } 202 /* no match found; *result will be the insert-after position */ 203 if (result) *result = low; 204 return -1; 205 } 206 207 /* 208 * Initialize the process's __env_owned structure 209 */ 210 __private_extern__ int 211 __init__env_owned_locked(int should_set_errno) 212 { 213 int save; 214 215 if (__env_owned) return 0; 216 217 if (!should_set_errno) 218 save = errno; 219 __env_owned = _owned_ptr_alloc(); 220 if (!__env_owned) { 221 if (!should_set_errno) 222 errno = save; 223 return -1; 224 } 225 return 0; 226 } 227 228 /* 229 * The copy flag may have 3 values: 230 * 1 - make a copy of the name/value pair 231 * 0 - take the name as a user-supplied name=value string 232 * -1 - like 0, except we copy of the name=value string in name 233 */ 234 __private_extern__ int 235 __setenv_locked(name, value, rewrite, copy, environp, owned) 236 const char *name; 237 const char *value; 238 int rewrite, copy; 239 char ***environp; 240 struct owned_ptr *owned; 241 { 242 char *c; 243 int offset; 244 int oindex; 245 246 if ((c = __findenv_locked(name, &offset, *environp))) { /* find if already exists */ 247 char *e; 248 if (!rewrite) 249 return (0); 250 /* 251 * In UNIX03, we can overwrite only if we allocated the 252 * string. Then we can realloc it if it is too small. 253 */ 254 e = (*environp)[offset]; 255 if (_owned_ptr_search(owned, e, &oindex) == 0) { 256 if (copy > 0) { 257 size_t l_value = strlen(value); 258 if (strlen(c) < l_value) { /* old smaller; resize*/ 259 char *r; 260 size_t len = c - e; 261 if ((r = realloc(e, l_value + len + 1)) == NULL) 262 return (-1); 263 if (r != e) { 264 (*environp)[offset] = r; 265 c = r + len; 266 _owned_ptr_delete(owned, oindex); 267 _owned_ptr_add(owned, r); 268 } 269 } 270 while ( (*c++ = *value++) ); 271 return (0); 272 } 273 /* Free up the slot for later use (putenv) */ 274 _owned_ptr_delete(owned, oindex); 275 free(e); 276 } 277 } else { /* create new slot */ 278 int cnt; 279 char **p; 280 281 for (p = *environp, cnt = 0; *p; ++p, ++cnt); 282 if (_owned_ptr_search(owned, *environp, &oindex) == 0) { /* just increase size */ 283 p = (char **)realloc((char *)*environp, 284 (size_t)(sizeof(char *) * (cnt + 2))); 285 if (!p) 286 return (-1); 287 if (*environp != p) { 288 _owned_ptr_delete(owned, oindex); 289 _owned_ptr_add(owned, p); 290 *environp = p; 291 } 292 } 293 else { /* get new space */ 294 /* copy old entries into it */ 295 p = malloc((size_t)(sizeof(char *) * (cnt + 2))); 296 if (!p) 297 return (-1); 298 _owned_ptr_add(owned, p); 299 bcopy(*environp, p, cnt * sizeof(char *)); 300 *environp = p; 301 } 302 (*environp)[cnt + 1] = NULL; 303 offset = cnt; 304 } 305 /* For non Unix03, or UnixO3 setenv(), we make a copy of the user's 306 * strings. For Unix03 putenv(), we put the string directly in 307 * the environment. */ 308 if (copy > 0) { 309 for (c = (char *)name; *c && *c != '='; ++c); /* no `=' in name */ 310 if (!((*environp)[offset] = /* name + `=' + value */ 311 malloc((size_t)((int)(c - name) + strlen(value) + 2)))) 312 return (-1); 313 _owned_ptr_add(owned, (*environp)[offset]); 314 for (c = (*environp)[offset]; (*c = *name++) && *c != '='; ++c); 315 for (*c++ = '='; (*c++ = *value++); ); 316 } else { 317 /* the legacy behavior copies the string */ 318 if (copy < 0) { 319 size_t len = strlen(name); 320 if((c = malloc(len + 1)) == NULL) 321 return (-1); 322 _owned_ptr_add(owned, c); 323 memcpy(c, name, len + 1); 324 name = c; 325 } 326 (*environp)[offset] = (char *)name; 327 } 328 return (0); 329 } 330 331 __private_extern__ void 332 __unsetenv_locked(const char *name, char **environ, struct owned_ptr *owned) 333 { 334 char **p; 335 int offset; 336 int oindex; 337 338 while (__findenv_locked(name, &offset, environ)) { /* if set multiple times */ 339 /* if we malloc-ed it, free it first */ 340 if (_owned_ptr_search(owned, environ[offset], &oindex) == 0) { 341 _owned_ptr_delete(owned, oindex); 342 free(environ[offset]); 343 } 344 for (p = &environ[offset];; ++p) 345 if (!(*p = *(p + 1))) 346 break; 347 } 348 } 349 350 /****************************************************************************/ 351 /* 352 * _allocenvstate -- SPI that creates a new state (opaque) 353 */ 354 void * 355 _allocenvstate(void) 356 { 357 return _owned_ptr_alloc(); 358 } 359 360 /* 361 * _copyenv -- SPI that copies a NULL-tereminated char * array in a newly 362 * allocated buffer, compatible with the other SPI env routines. If env 363 * is NULL, a char * array composed of a single NULL is returned. NULL 364 * is returned on error. (This isn't needed anymore, as __setenv_locked will 365 * automatically make a copy.) 366 */ 367 char ** 368 _copyenv(char **env) 369 { 370 char **p; 371 int cnt = 1; 372 373 if (env) 374 for (p = env; *p; ++p, ++cnt); 375 p = (char **)malloc((size_t)(sizeof(char *) * cnt)); 376 if (!p) 377 return (NULL); 378 if (env) 379 bcopy(env, p, cnt * sizeof(char *)); 380 else 381 *p = NULL; 382 return p; 383 } 384 385 /* 386 * _deallocenvstate -- SPI that frees all the memory associated with the state 387 * and all allocated strings, including the environment array itself if it 388 * was copied. 389 */ 390 int 391 _deallocenvstate(void *state) 392 { 393 struct owned_ptr *owned; 394 395 if (!(owned = (struct owned_ptr *)state) || owned == __env_owned) { 396 errno = EINVAL; 397 return -1; 398 } 399 _owned_ptr_free(owned); 400 return 0; 401 } 402 403 /* 404 * setenvp -- SPI using an arbitrary pointer to string array and an env state, 405 * created by _allocenvstate(). Initial checking is not done. 406 * 407 * Set the value of the environmental variable "name" to be 408 * "value". If rewrite is set, replace any current value. 409 */ 410 int 411 _setenvp(const char *name, const char *value, int rewrite, char ***envp, void *state) 412 { 413 __environ_lock(); 414 if (__init__env_owned_locked(1)) { 415 __environ_unlock(); 416 return (-1); 417 } 418 int ret = __setenv_locked(name, value, rewrite, 1, envp, 419 (state ? (struct owned_ptr *)state : __env_owned)); 420 __environ_unlock(); 421 return ret; 422 } 423 424 /* 425 * unsetenv(name) -- SPI using an arbitrary pointer to string array and an env 426 * state, created by _allocenvstate(). Initial checking is not done. 427 * 428 * Delete environmental variable "name". 429 */ 430 int 431 _unsetenvp(const char *name, char ***envp, void *state) 432 { 433 __environ_lock(); 434 if (__init__env_owned_locked(1)) { 435 __environ_unlock(); 436 return (-1); 437 } 438 __unsetenv_locked(name, *envp, (state ? (struct owned_ptr *)state : __env_owned)); 439 __environ_unlock(); 440 return 0; 441 } 442 443 #endif /* !BUILD_VARIANT */ 444 445 /* 446 * setenv -- 447 * Set the value of the environmental variable "name" to be 448 * "value". If rewrite is set, replace any current value. 449 */ 450 int 451 setenv(name, value, rewrite) 452 const char *name; 453 const char *value; 454 int rewrite; 455 { 456 int ret; 457 458 /* no null ptr or empty str */ 459 if(name == NULL || *name == 0) { 460 errno = EINVAL; 461 return (-1); 462 } 463 464 #if __DARWIN_UNIX03 465 /* no '=' in name */ 466 if (strchr(name, '=')) { 467 errno = EINVAL; 468 return (-1); 469 } 470 #endif /* __DARWIN_UNIX03 */ 471 472 __environ_lock(); 473 if (__init__env_owned_locked(1)) { 474 __environ_unlock(); 475 return (-1); 476 } 477 ret = __setenv_locked(name, value, rewrite, 1, _NSGetEnviron(), __env_owned); 478 #ifdef LEGACY_CRT1_ENVIRON 479 _saved_environ = *_NSGetEnviron(); 480 #endif /* !LEGACY_CRT1_ENVIRON */ 481 __environ_unlock(); 482 483 return ret; 484 } 485 486 static inline __attribute__((always_inline)) int 487 _unsetenv(const char *name, int should_set_errno) 488 { 489 __environ_lock(); 490 if (__init__env_owned_locked(should_set_errno)) { 491 __environ_unlock(); 492 return (-1); 493 } 494 __unsetenv_locked(name, *_NSGetEnviron(), __env_owned); 495 __environ_unlock(); 496 return 0; 497 } 498 499 /* 500 * unsetenv(name) -- 501 * Delete environmental variable "name". 502 */ 503 #if __DARWIN_UNIX03 504 int 505 unsetenv(const char *name) 506 { 507 /* no null ptr or empty str */ 508 if(name == NULL || *name == 0) { 509 errno = EINVAL; 510 return (-1); 511 } 512 513 /* no '=' in name */ 514 if (strchr(name, '=')) { 515 errno = EINVAL; 516 return (-1); 517 } 518 return _unsetenv(name, 1); 519 } 520 #else /* !__DARWIN_UNIX03 */ 521 void 522 unsetenv(const char *name) 523 { 524 /* no null ptr or empty str */ 525 if(name == NULL || *name == 0) 526 return; 527 _unsetenv(name, 0); 528 } 529 #endif /* __DARWIN_UNIX03 */