getenv.c revision 200191
1/*- 2 * Copyright (c) 2007-2009 Sean C. Farley <scf@FreeBSD.org> 3 * 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 * without modification, immediately at the beginning of the file. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/lib/libc/stdlib/getenv.c 200191 2009-12-06 23:27:24Z scf $"); 29 30 31#include "namespace.h" 32#include <sys/types.h> 33#include <errno.h> 34#include <stdbool.h> 35#include <stddef.h> 36#include <stdlib.h> 37#include <string.h> 38#include <unistd.h> 39#include "un-namespace.h" 40 41 42static const char CorruptEnvFindMsg[] = "environment corrupt; unable to find "; 43static const char CorruptEnvValueMsg[] = 44 "environment corrupt; missing value for "; 45 46 47/* 48 * Standard environ. environ variable is exposed to entire process. 49 * 50 * origEnviron: Upon cleanup on unloading of library or failure, this 51 * allows environ to return to as it was before. 52 * environSize: Number of variables environ can hold. Can only 53 * increase. 54 * intEnviron: Internally-built environ. Exposed via environ during 55 * (re)builds of the environment. 56 */ 57extern char **environ; 58static char **origEnviron; 59static char **intEnviron = NULL; 60static int environSize = 0; 61 62/* 63 * Array of environment variables built from environ. Each element records: 64 * name: Pointer to name=value string 65 * name length: Length of name not counting '=' character 66 * value: Pointer to value within same string as name 67 * value size: Size (not length) of space for value not counting the 68 * nul character 69 * active state: true/false value to signify whether variable is active. 70 * Useful since multiple variables with the same name can 71 * co-exist. At most, one variable can be active at any 72 * one time. 73 * putenv: Created from putenv() call. This memory must not be 74 * reused. 75 */ 76static struct envVars { 77 size_t nameLen; 78 size_t valueSize; 79 char *name; 80 char *value; 81 bool active; 82 bool putenv; 83} *envVars = NULL; 84 85/* 86 * Environment array information. 87 * 88 * envActive: Number of active variables in array. 89 * envVarsSize: Size of array. 90 * envVarsTotal: Number of total variables in array (active or not). 91 */ 92static int envActive = 0; 93static int envVarsSize = 0; 94static int envVarsTotal = 0; 95 96 97/* Deinitialization of new environment. */ 98static void __attribute__ ((destructor)) __clean_env_destructor(void); 99 100 101/* 102 * A simple version of warnx() to avoid the bloat of including stdio in static 103 * binaries. 104 */ 105static void 106__env_warnx(const char *msg, const char *name, size_t nameLen) 107{ 108 static const char nl[] = "\n"; 109 static const char progSep[] = ": "; 110 111 _write(STDERR_FILENO, _getprogname(), strlen(_getprogname())); 112 _write(STDERR_FILENO, progSep, sizeof(progSep) - 1); 113 _write(STDERR_FILENO, msg, strlen(msg)); 114 _write(STDERR_FILENO, name, nameLen); 115 _write(STDERR_FILENO, nl, sizeof(nl) - 1); 116 117 return; 118} 119 120 121/* 122 * Inline strlen() for performance. Also, perform check for an equals sign. 123 * Cheaper here than peforming a strchr() later. 124 */ 125static inline size_t 126__strleneq(const char *str) 127{ 128 const char *s; 129 130 for (s = str; *s != '\0'; ++s) 131 if (*s == '=') 132 return (0); 133 134 return (s - str); 135} 136 137 138/* 139 * Comparison of an environment name=value to a name. 140 */ 141static inline bool 142strncmpeq(const char *nameValue, const char *name, size_t nameLen) 143{ 144 if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=') 145 return (true); 146 147 return (false); 148} 149 150 151/* 152 * Using environment, returns pointer to value associated with name, if any, 153 * else NULL. If the onlyActive flag is set to true, only variables that are 154 * active are returned else all are. 155 */ 156static inline char * 157__findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive) 158{ 159 int ndx; 160 161 /* 162 * Find environment variable from end of array (more likely to be 163 * active). A variable created by putenv is always active, or it is not 164 * tracked in the array. 165 */ 166 for (ndx = *envNdx; ndx >= 0; ndx--) 167 if (envVars[ndx].putenv) { 168 if (strncmpeq(envVars[ndx].name, name, nameLen)) { 169 *envNdx = ndx; 170 return (envVars[ndx].name + nameLen + 171 sizeof ("=") - 1); 172 } 173 } else if ((!onlyActive || envVars[ndx].active) && 174 (envVars[ndx].nameLen == nameLen && 175 strncmpeq(envVars[ndx].name, name, nameLen))) { 176 *envNdx = ndx; 177 return (envVars[ndx].value); 178 } 179 180 return (NULL); 181} 182 183 184/* 185 * Using environ, returns pointer to value associated with name, if any, else 186 * NULL. Used on the original environ passed into the program. 187 */ 188static char * 189__findenv_environ(const char *name, size_t nameLen) 190{ 191 int envNdx; 192 193 /* Find variable within environ. */ 194 for (envNdx = 0; environ[envNdx] != NULL; envNdx++) 195 if (strncmpeq(environ[envNdx], name, nameLen)) 196 return (&(environ[envNdx][nameLen + sizeof("=") - 1])); 197 198 return (NULL); 199} 200 201 202/* 203 * Remove variable added by putenv() from variable tracking array. 204 */ 205static void 206__remove_putenv(int envNdx) 207{ 208 envVarsTotal--; 209 if (envVarsTotal > envNdx) 210 memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]), 211 (envVarsTotal - envNdx) * sizeof (*envVars)); 212 memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars)); 213 214 return; 215} 216 217 218/* 219 * Deallocate the environment built from environ as well as environ then set 220 * both to NULL. Eases debugging of memory leaks. 221 */ 222static void 223__clean_env(bool freeVars) 224{ 225 int envNdx; 226 227 /* Deallocate environment and environ if created by *env(). */ 228 if (envVars != NULL) { 229 for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) 230 /* Free variables or deactivate them. */ 231 if (envVars[envNdx].putenv) { 232 if (!freeVars) 233 __remove_putenv(envNdx); 234 } else { 235 if (freeVars) 236 free(envVars[envNdx].name); 237 else 238 envVars[envNdx].active = false; 239 } 240 if (freeVars) { 241 free(envVars); 242 envVars = NULL; 243 } else 244 envActive = 0; 245 246 /* Restore original environ if it has not updated by program. */ 247 if (origEnviron != NULL) { 248 if (environ == intEnviron) 249 environ = origEnviron; 250 free(intEnviron); 251 intEnviron = NULL; 252 environSize = 0; 253 } 254 } 255 256 return; 257} 258 259 260/* 261 * Using the environment, rebuild the environ array for use by other C library 262 * calls that depend upon it. 263 */ 264static int 265__rebuild_environ(int newEnvironSize) 266{ 267 char **tmpEnviron; 268 int envNdx; 269 int environNdx; 270 int tmpEnvironSize; 271 272 /* Resize environ. */ 273 if (newEnvironSize > environSize) { 274 tmpEnvironSize = newEnvironSize * 2; 275 tmpEnviron = realloc(intEnviron, sizeof (*intEnviron) * 276 (tmpEnvironSize + 1)); 277 if (tmpEnviron == NULL) 278 return (-1); 279 environSize = tmpEnvironSize; 280 intEnviron = tmpEnviron; 281 } 282 envActive = newEnvironSize; 283 284 /* Assign active variables to environ. */ 285 for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--) 286 if (envVars[envNdx].active) 287 intEnviron[environNdx++] = envVars[envNdx].name; 288 intEnviron[environNdx] = NULL; 289 290 /* Always set environ which may have been replaced by program. */ 291 environ = intEnviron; 292 293 return (0); 294} 295 296 297/* 298 * Enlarge new environment. 299 */ 300static inline bool 301__enlarge_env(void) 302{ 303 int newEnvVarsSize; 304 struct envVars *tmpEnvVars; 305 306 envVarsTotal++; 307 if (envVarsTotal > envVarsSize) { 308 newEnvVarsSize = envVarsTotal * 2; 309 tmpEnvVars = realloc(envVars, sizeof (*envVars) * 310 newEnvVarsSize); 311 if (tmpEnvVars == NULL) { 312 envVarsTotal--; 313 return (false); 314 } 315 envVarsSize = newEnvVarsSize; 316 envVars = tmpEnvVars; 317 } 318 319 return (true); 320} 321 322 323/* 324 * Using environ, build an environment for use by standard C library calls. 325 */ 326static int 327__build_env(void) 328{ 329 char **env; 330 int activeNdx; 331 int envNdx; 332 int savedErrno; 333 size_t nameLen; 334 335 /* Check for non-existant environment. */ 336 if (environ == NULL || environ[0] == NULL) 337 return (0); 338 339 /* Count environment variables. */ 340 for (env = environ, envVarsTotal = 0; *env != NULL; env++) 341 envVarsTotal++; 342 envVarsSize = envVarsTotal * 2; 343 344 /* Create new environment. */ 345 envVars = calloc(1, sizeof (*envVars) * envVarsSize); 346 if (envVars == NULL) 347 goto Failure; 348 349 /* Copy environ values and keep track of them. */ 350 for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) { 351 envVars[envNdx].putenv = false; 352 envVars[envNdx].name = 353 strdup(environ[envVarsTotal - envNdx - 1]); 354 if (envVars[envNdx].name == NULL) 355 goto Failure; 356 envVars[envNdx].value = strchr(envVars[envNdx].name, '='); 357 if (envVars[envNdx].value != NULL) { 358 envVars[envNdx].value++; 359 envVars[envNdx].valueSize = 360 strlen(envVars[envNdx].value); 361 } else { 362 __env_warnx(CorruptEnvValueMsg, envVars[envNdx].name, 363 strlen(envVars[envNdx].name)); 364 continue; 365 } 366 367 /* 368 * Find most current version of variable to make active. This 369 * will prevent multiple active variables from being created 370 * during this initialization phase. 371 */ 372 nameLen = envVars[envNdx].value - envVars[envNdx].name - 1; 373 envVars[envNdx].nameLen = nameLen; 374 activeNdx = envVarsTotal - 1; 375 if (__findenv(envVars[envNdx].name, nameLen, &activeNdx, 376 false) == NULL) { 377 __env_warnx(CorruptEnvFindMsg, envVars[envNdx].name, 378 nameLen); 379 continue; 380 } 381 envVars[activeNdx].active = true; 382 } 383 384 /* Create a new environ. */ 385 origEnviron = environ; 386 environ = NULL; 387 if (__rebuild_environ(envVarsTotal) == 0) 388 return (0); 389 390Failure: 391 savedErrno = errno; 392 __clean_env(true); 393 errno = savedErrno; 394 395 return (-1); 396} 397 398 399/* 400 * Destructor function with default argument to __clean_env(). 401 */ 402static void 403__clean_env_destructor(void) 404{ 405 __clean_env(true); 406 407 return; 408} 409 410 411/* 412 * Returns the value of a variable or NULL if none are found. 413 */ 414char * 415getenv(const char *name) 416{ 417 int envNdx; 418 size_t nameLen; 419 420 /* Check for malformed name. */ 421 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 422 errno = EINVAL; 423 return (NULL); 424 } 425 426 /* 427 * Variable search order: 428 * 1. Check for an empty environ. This allows an application to clear 429 * the environment. 430 * 2. Search the external environ array. 431 * 3. Search the internal environment. 432 * 433 * Since malloc() depends upon getenv(), getenv() must never cause the 434 * internal environment storage to be generated. 435 */ 436 if (environ == NULL || environ[0] == NULL) 437 return (NULL); 438 else if (envVars == NULL || environ != intEnviron) 439 return (__findenv_environ(name, nameLen)); 440 else { 441 envNdx = envVarsTotal - 1; 442 return (__findenv(name, nameLen, &envNdx, true)); 443 } 444} 445 446 447/* 448 * Set the value of a variable. Older settings are labeled as inactive. If an 449 * older setting has enough room to store the new value, it will be reused. No 450 * previous variables are ever freed here to avoid causing a segmentation fault 451 * in a user's code. 452 * 453 * The variables nameLen and valueLen are passed into here to allow the caller 454 * to calculate the length by means besides just strlen(). 455 */ 456static int 457__setenv(const char *name, size_t nameLen, const char *value, int overwrite) 458{ 459 bool reuse; 460 char *env; 461 int envNdx; 462 int newEnvActive; 463 size_t valueLen; 464 465 /* Find existing environment variable large enough to use. */ 466 envNdx = envVarsTotal - 1; 467 newEnvActive = envActive; 468 valueLen = strlen(value); 469 reuse = false; 470 if (__findenv(name, nameLen, &envNdx, false) != NULL) { 471 /* Deactivate entry if overwrite is allowed. */ 472 if (envVars[envNdx].active) { 473 if (overwrite == 0) 474 return (0); 475 envVars[envNdx].active = false; 476 newEnvActive--; 477 } 478 479 /* putenv() created variable cannot be reused. */ 480 if (envVars[envNdx].putenv) 481 __remove_putenv(envNdx); 482 483 /* Entry is large enough to reuse. */ 484 else if (envVars[envNdx].valueSize >= valueLen) 485 reuse = true; 486 } 487 488 /* Create new variable if none was found of sufficient size. */ 489 if (! reuse) { 490 /* Enlarge environment. */ 491 envNdx = envVarsTotal; 492 if (!__enlarge_env()) 493 return (-1); 494 495 /* Create environment entry. */ 496 envVars[envNdx].name = malloc(nameLen + sizeof ("=") + 497 valueLen); 498 if (envVars[envNdx].name == NULL) { 499 envVarsTotal--; 500 return (-1); 501 } 502 envVars[envNdx].nameLen = nameLen; 503 envVars[envNdx].valueSize = valueLen; 504 505 /* Save name of name/value pair. */ 506 env = stpcpy(envVars[envNdx].name, name); 507 if ((envVars[envNdx].name)[nameLen] != '=') 508 env = stpcpy(env, "="); 509 } 510 else 511 env = envVars[envNdx].value; 512 513 /* Save value of name/value pair. */ 514 strcpy(env, value); 515 envVars[envNdx].value = env; 516 envVars[envNdx].active = true; 517 newEnvActive++; 518 519 /* No need to rebuild environ if an active variable was reused. */ 520 if (reuse && newEnvActive == envActive) 521 return (0); 522 else 523 return (__rebuild_environ(newEnvActive)); 524} 525 526 527/* 528 * If the program attempts to replace the array of environment variables 529 * (environ) environ or sets the first varible to NULL, then deactivate all 530 * variables and merge in the new list from environ. 531 */ 532static int 533__merge_environ(void) 534{ 535 char **env; 536 char *equals; 537 538 /* 539 * Internally-built environ has been replaced or cleared (detected by 540 * using the count of active variables against a NULL as the first value 541 * in environ). Clean up everything. 542 */ 543 if (intEnviron != NULL && (environ != intEnviron || (envActive > 0 && 544 environ[0] == NULL))) { 545 /* Deactivate all environment variables. */ 546 if (envActive > 0) { 547 origEnviron = NULL; 548 __clean_env(false); 549 } 550 551 /* 552 * Insert new environ into existing, yet deactivated, 553 * environment array. 554 */ 555 origEnviron = environ; 556 if (origEnviron != NULL) 557 for (env = origEnviron; *env != NULL; env++) { 558 if ((equals = strchr(*env, '=')) == NULL) { 559 __env_warnx(CorruptEnvValueMsg, *env, 560 strlen(*env)); 561 continue; 562 } 563 if (__setenv(*env, equals - *env, equals + 1, 564 1) == -1) 565 return (-1); 566 } 567 } 568 569 return (0); 570} 571 572 573/* 574 * The exposed setenv() that peforms a few tests before calling the function 575 * (__setenv()) that does the actual work of inserting a variable into the 576 * environment. 577 */ 578int 579setenv(const char *name, const char *value, int overwrite) 580{ 581 size_t nameLen; 582 583 /* Check for malformed name. */ 584 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 585 errno = EINVAL; 586 return (-1); 587 } 588 589 /* Initialize environment. */ 590 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 591 return (-1); 592 593 return (__setenv(name, nameLen, value, overwrite)); 594} 595 596 597/* 598 * Insert a "name=value" string into the environment. Special settings must be 599 * made to keep setenv() from reusing this memory block and unsetenv() from 600 * allowing it to be tracked. 601 */ 602int 603putenv(char *string) 604{ 605 char *equals; 606 int envNdx; 607 int newEnvActive; 608 size_t nameLen; 609 610 /* Check for malformed argument. */ 611 if (string == NULL || (equals = strchr(string, '=')) == NULL || 612 (nameLen = equals - string) == 0) { 613 errno = EINVAL; 614 return (-1); 615 } 616 617 /* Initialize environment. */ 618 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 619 return (-1); 620 621 /* Deactivate previous environment variable. */ 622 envNdx = envVarsTotal - 1; 623 newEnvActive = envActive; 624 if (__findenv(string, nameLen, &envNdx, true) != NULL) { 625 /* Reuse previous putenv slot. */ 626 if (envVars[envNdx].putenv) { 627 envVars[envNdx].name = string; 628 return (__rebuild_environ(envActive)); 629 } else { 630 newEnvActive--; 631 envVars[envNdx].active = false; 632 } 633 } 634 635 /* Enlarge environment. */ 636 envNdx = envVarsTotal; 637 if (!__enlarge_env()) 638 return (-1); 639 640 /* Create environment entry. */ 641 envVars[envNdx].name = string; 642 envVars[envNdx].nameLen = -1; 643 envVars[envNdx].value = NULL; 644 envVars[envNdx].valueSize = -1; 645 envVars[envNdx].putenv = true; 646 envVars[envNdx].active = true; 647 newEnvActive++; 648 649 return (__rebuild_environ(newEnvActive)); 650} 651 652 653/* 654 * Unset variable with the same name by flagging it as inactive. No variable is 655 * ever freed. 656 */ 657int 658unsetenv(const char *name) 659{ 660 int envNdx; 661 size_t nameLen; 662 663 /* Check for malformed name. */ 664 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 665 errno = EINVAL; 666 return (-1); 667 } 668 669 /* Initialize environment. */ 670 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 671 return (-1); 672 673 /* Deactivate specified variable. */ 674 envNdx = envVarsTotal - 1; 675 if (__findenv(name, nameLen, &envNdx, true) != NULL) { 676 envVars[envNdx].active = false; 677 if (envVars[envNdx].putenv) 678 __remove_putenv(envNdx); 679 __rebuild_environ(envActive - 1); 680 } 681 682 return (0); 683} 684