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