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 * $FreeBSD: head/lib/libc/stdlib/getenv.c 253413 2013-07-17 08:45:27Z avg $ 27 */ 28 29 #include "namespace.h" 30 #include <sys/types.h> 31 #include <errno.h> 32 #include <stdbool.h> 33 #include <stddef.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <unistd.h> 37 #include "un-namespace.h" 38 39 40 static const char CorruptEnvFindMsg[] = "environment corrupt; unable to find "; 41 static const char CorruptEnvValueMsg[] = 42 "environment corrupt; missing value for "; 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 */ 55 extern char **environ; 56 static char **origEnviron; 57 static char **intEnviron = NULL; 58 static 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 */ 74 static 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 */ 90 static int envActive = 0; 91 static int envVarsSize = 0; 92 static int envVarsTotal = 0; 93 94 95 /* Deinitialization of new environment. */ 96 static void __attribute__ ((destructor)) __clean_env_destructor(void); 97 98 99 /* 100 * A simple version of warnx() to avoid the bloat of including stdio in static 101 * binaries. 102 */ 103 static void 104 __env_warnx(const char *msg, const char *name, size_t nameLen) 105 { 106 static const char nl[] = "\n"; 107 static const char progSep[] = ": "; 108 109 _write(STDERR_FILENO, _getprogname(), strlen(_getprogname())); 110 _write(STDERR_FILENO, progSep, sizeof(progSep) - 1); 111 _write(STDERR_FILENO, msg, strlen(msg)); 112 _write(STDERR_FILENO, name, nameLen); 113 _write(STDERR_FILENO, nl, sizeof(nl) - 1); 114 115 return; 116 } 117 118 119 /* 120 * Inline strlen() for performance. Also, perform check for an equals sign. 121 * Cheaper here than peforming a strchr() later. 122 */ 123 static inline size_t 124 __strleneq(const char *str) 125 { 126 const char *s; 127 128 for (s = str; *s != '\0'; ++s) 129 if (*s == '=') 130 return (0); 131 132 return (s - str); 133 } 134 135 136 /* 137 * Comparison of an environment name=value to a name. 138 */ 139 static inline bool 140 strncmpeq(const char *nameValue, const char *name, size_t nameLen) 141 { 142 if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=') 143 return (true); 144 145 return (false); 146 } 147 148 149 /* 150 * Using environment, returns pointer to value associated with name, if any, 151 * else NULL. If the onlyActive flag is set to true, only variables that are 152 * active are returned else all are. 153 */ 154 static inline char * 155 __findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive) 156 { 157 int ndx; 158 159 /* 160 * Find environment variable from end of array (more likely to be 161 * active). A variable created by putenv is always active, or it is not 162 * tracked in the array. 163 */ 164 for (ndx = *envNdx; ndx >= 0; ndx--) 165 if (envVars[ndx].putenv) { 166 if (strncmpeq(envVars[ndx].name, name, nameLen)) { 167 *envNdx = ndx; 168 return (envVars[ndx].name + nameLen + 169 sizeof ("=") - 1); 170 } 171 } else if ((!onlyActive || envVars[ndx].active) && 172 (envVars[ndx].nameLen == nameLen && 173 strncmpeq(envVars[ndx].name, name, nameLen))) { 174 *envNdx = ndx; 175 return (envVars[ndx].value); 176 } 177 178 return (NULL); 179 } 180 181 182 /* 183 * Using environ, returns pointer to value associated with name, if any, else 184 * NULL. Used on the original environ passed into the program. 185 */ 186 static char * 187 __findenv_environ(const char *name, size_t nameLen) 188 { 189 int envNdx; 190 191 /* Find variable within environ. */ 192 for (envNdx = 0; environ[envNdx] != NULL; envNdx++) 193 if (strncmpeq(environ[envNdx], name, nameLen)) 194 return (&(environ[envNdx][nameLen + sizeof("=") - 1])); 195 196 return (NULL); 197 } 198 199 200 /* 201 * Remove variable added by putenv() from variable tracking array. 202 */ 203 static void 204 __remove_putenv(int envNdx) 205 { 206 envVarsTotal--; 207 if (envVarsTotal > envNdx) 208 memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]), 209 (envVarsTotal - envNdx) * sizeof (*envVars)); 210 memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars)); 211 212 return; 213 } 214 215 216 /* 217 * Deallocate the environment built from environ as well as environ then set 218 * both to NULL. Eases debugging of memory leaks. 219 */ 220 static void 221 __clean_env(bool freeVars) 222 { 223 int envNdx; 224 225 /* Deallocate environment and environ if created by *env(). */ 226 if (envVars != NULL) { 227 for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) 228 /* Free variables or deactivate them. */ 229 if (envVars[envNdx].putenv) { 230 if (!freeVars) 231 __remove_putenv(envNdx); 232 } else { 233 if (freeVars) 234 free(envVars[envNdx].name); 235 else 236 envVars[envNdx].active = false; 237 } 238 if (freeVars) { 239 free(envVars); 240 envVars = NULL; 241 } else 242 envActive = 0; 243 244 /* Restore original environ if it has not updated by program. */ 245 if (origEnviron != NULL) { 246 if (environ == intEnviron) 247 environ = origEnviron; 248 free(intEnviron); 249 intEnviron = NULL; 250 environSize = 0; 251 } 252 } 253 254 return; 255 } 256 257 258 /* 259 * Using the environment, rebuild the environ array for use by other C library 260 * calls that depend upon it. 261 */ 262 static int 263 __rebuild_environ(int newEnvironSize) 264 { 265 char **tmpEnviron; 266 int envNdx; 267 int environNdx; 268 int tmpEnvironSize; 269 270 /* Resize environ. */ 271 if (newEnvironSize > environSize) { 272 tmpEnvironSize = newEnvironSize * 2; 273 tmpEnviron = realloc(intEnviron, sizeof (*intEnviron) * 274 (tmpEnvironSize + 1)); 275 if (tmpEnviron == NULL) 276 return (-1); 277 environSize = tmpEnvironSize; 278 intEnviron = tmpEnviron; 279 } 280 envActive = newEnvironSize; 281 282 /* Assign active variables to environ. */ 283 for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--) 284 if (envVars[envNdx].active) 285 intEnviron[environNdx++] = envVars[envNdx].name; 286 intEnviron[environNdx] = NULL; 287 288 /* Always set environ which may have been replaced by program. */ 289 environ = intEnviron; 290 291 return (0); 292 } 293 294 295 /* 296 * Enlarge new environment. 297 */ 298 static inline bool 299 __enlarge_env(void) 300 { 301 int newEnvVarsSize; 302 struct envVars *tmpEnvVars; 303 304 envVarsTotal++; 305 if (envVarsTotal > envVarsSize) { 306 newEnvVarsSize = envVarsTotal * 2; 307 tmpEnvVars = realloc(envVars, sizeof (*envVars) * 308 newEnvVarsSize); 309 if (tmpEnvVars == NULL) { 310 envVarsTotal--; 311 return (false); 312 } 313 envVarsSize = newEnvVarsSize; 314 envVars = tmpEnvVars; 315 } 316 317 return (true); 318 } 319 320 321 /* 322 * Using environ, build an environment for use by standard C library calls. 323 */ 324 static int 325 __build_env(void) 326 { 327 char **env; 328 int activeNdx; 329 int envNdx; 330 int savedErrno; 331 size_t nameLen; 332 333 /* Check for non-existant environment. */ 334 if (environ == NULL || environ[0] == NULL) 335 return (0); 336 337 /* Count environment variables. */ 338 for (env = environ, envVarsTotal = 0; *env != NULL; env++) 339 envVarsTotal++; 340 envVarsSize = envVarsTotal * 2; 341 342 /* Create new environment. */ 343 envVars = calloc(1, sizeof (*envVars) * envVarsSize); 344 if (envVars == NULL) 345 goto Failure; 346 347 /* Copy environ values and keep track of them. */ 348 for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) { 349 envVars[envNdx].putenv = false; 350 envVars[envNdx].name = 351 strdup(environ[envVarsTotal - envNdx - 1]); 352 if (envVars[envNdx].name == NULL) 353 goto Failure; 354 envVars[envNdx].value = strchr(envVars[envNdx].name, '='); 355 if (envVars[envNdx].value != NULL) { 356 envVars[envNdx].value++; 357 envVars[envNdx].valueSize = 358 strlen(envVars[envNdx].value); 359 } else { 360 __env_warnx(CorruptEnvValueMsg, envVars[envNdx].name, 361 strlen(envVars[envNdx].name)); 362 errno = EFAULT; 363 goto Failure; 364 } 365 366 /* 367 * Find most current version of variable to make active. This 368 * will prevent multiple active variables from being created 369 * during this initialization phase. 370 */ 371 nameLen = envVars[envNdx].value - envVars[envNdx].name - 1; 372 envVars[envNdx].nameLen = nameLen; 373 activeNdx = envVarsTotal - 1; 374 if (__findenv(envVars[envNdx].name, nameLen, &activeNdx, 375 false) == NULL) { 376 __env_warnx(CorruptEnvFindMsg, envVars[envNdx].name, 377 nameLen); 378 errno = EFAULT; 379 goto Failure; 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 390 Failure: 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 */ 402 static 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 */ 414 char * 415 getenv(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 */ 456 static 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 = stpncpy(envVars[envNdx].name, name, nameLen); 507 *env++ = '='; 508 } 509 else 510 env = envVars[envNdx].value; 511 512 /* Save value of name/value pair. */ 513 strcpy(env, value); 514 envVars[envNdx].value = env; 515 envVars[envNdx].active = true; 516 newEnvActive++; 517 518 /* No need to rebuild environ if an active variable was reused. */ 519 if (reuse && newEnvActive == envActive) 520 return (0); 521 else 522 return (__rebuild_environ(newEnvActive)); 523 } 524 525 526 /* 527 * If the program attempts to replace the array of environment variables 528 * (environ) environ or sets the first varible to NULL, then deactivate all 529 * variables and merge in the new list from environ. 530 */ 531 static int 532 __merge_environ(void) 533 { 534 char **env; 535 char *equals; 536 537 /* 538 * Internally-built environ has been replaced or cleared (detected by 539 * using the count of active variables against a NULL as the first value 540 * in environ). Clean up everything. 541 */ 542 if (intEnviron != NULL && (environ != intEnviron || (envActive > 0 && 543 environ[0] == NULL))) { 544 /* Deactivate all environment variables. */ 545 if (envActive > 0) { 546 origEnviron = NULL; 547 __clean_env(false); 548 } 549 550 /* 551 * Insert new environ into existing, yet deactivated, 552 * environment array. 553 */ 554 origEnviron = environ; 555 if (origEnviron != NULL) 556 for (env = origEnviron; *env != NULL; env++) { 557 if ((equals = strchr(*env, '=')) == NULL) { 558 __env_warnx(CorruptEnvValueMsg, *env, 559 strlen(*env)); 560 errno = EFAULT; 561 return (-1); 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 */ 578 int 579 setenv(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 */ 602 int 603 putenv(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 */ 657 int 658 unsetenv(const char *name) 659 { 660 int envNdx; 661 size_t nameLen; 662 int newEnvActive; 663 664 /* Check for malformed name. */ 665 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 666 errno = EINVAL; 667 return (-1); 668 } 669 670 /* Initialize environment. */ 671 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 672 return (-1); 673 674 /* Deactivate specified variable. */ 675 /* Remove all occurrences. */ 676 envNdx = envVarsTotal - 1; 677 newEnvActive = envActive; 678 while (__findenv(name, nameLen, &envNdx, true) != NULL) { 679 envVars[envNdx].active = false; 680 if (envVars[envNdx].putenv) 681 __remove_putenv(envNdx); 682 envNdx--; 683 newEnvActive--; 684 } 685 if (newEnvActive != envActive) 686 __rebuild_environ(newEnvActive); 687 688 return (0); 689 } 690