1 /*- 2 * Copyright (c) 2007-2008 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: src/lib/libc/stdlib/getenv.c,v 1.15 2008/08/03 22:47:23 scf Exp $ 27 * $DragonFly: src/lib/libc/stdlib/getenv.c,v 1.5 2005/04/28 13:51:55 joerg Exp $ 28 */ 29 30 #include "namespace.h" 31 #include <sys/types.h> 32 #include <errno.h> 33 #include <stdbool.h> 34 #include <stddef.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 #include "un-namespace.h" 39 40 41 static const char CorruptEnvFindMsg[] = "environment corrupt; unable to find "; 42 static const char CorruptEnvValueMsg[] = 43 "environment corrupt; missing value for "; 44 45 46 /* 47 * Standard environ. environ variable is exposed to entire process. 48 * 49 * origEnviron: Upon cleanup on unloading of library or failure, this 50 * allows environ to return to as it was before. 51 * environSize: Number of variables environ can hold. Can only 52 * increase. 53 * intEnviron: Internally-built environ. Exposed via environ during 54 * (re)builds of the environment. 55 */ 56 extern char **environ; 57 static char **origEnviron; 58 static char **intEnviron = NULL; 59 static int environSize = 0; 60 61 /* 62 * Array of environment variables built from environ. Each element records: 63 * name: Pointer to name=value string 64 * name length: Length of name not counting '=' character 65 * value: Pointer to value within same string as name 66 * value size: Size (not length) of space for value not counting the 67 * nul character 68 * active state: true/false value to signify whether variable is active. 69 * Useful since multiple variables with the same name can 70 * co-exist. At most, one variable can be active at any 71 * one time. 72 * putenv: Created from putenv() call. This memory must not be 73 * reused. 74 */ 75 static struct envVars { 76 size_t nameLen; 77 size_t valueSize; 78 char *name; 79 char *value; 80 bool active; 81 bool putenv; 82 } *envVars = NULL; 83 84 /* 85 * Environment array information. 86 * 87 * envActive: Number of active variables in array. 88 * envVarsSize: Size of array. 89 * envVarsTotal: Number of total variables in array (active or not). 90 */ 91 static int envActive = 0; 92 static int envVarsSize = 0; 93 static int envVarsTotal = 0; 94 95 96 /* Deinitialization of new environment. */ 97 static void __attribute__ ((destructor)) __clean_env_destructor(void); 98 99 100 /* 101 * A simple version of warnx() to avoid the bloat of including stdio in static 102 * binaries. 103 */ 104 static void 105 __env_warnx(const char *msg, const char *name, size_t nameLen) 106 { 107 static const char nl[] = "\n"; 108 static const char progSep[] = ": "; 109 110 _write(STDERR_FILENO, _getprogname(), strlen(_getprogname())); 111 _write(STDERR_FILENO, progSep, sizeof(progSep) - 1); 112 _write(STDERR_FILENO, msg, strlen(msg)); 113 _write(STDERR_FILENO, name, nameLen); 114 _write(STDERR_FILENO, nl, sizeof(nl) - 1); 115 116 return; 117 } 118 119 120 /* 121 * Inline strlen() for performance. Also, perform check for an equals sign. 122 * Cheaper here than peforming a strchr() later. 123 */ 124 static inline size_t 125 __strleneq(const char *str) 126 { 127 const char *s; 128 129 for (s = str; *s != '\0'; ++s) 130 if (*s == '=') 131 return (0); 132 133 return (s - str); 134 } 135 136 137 /* 138 * Comparison of an environment name=value to a name. 139 */ 140 static inline bool 141 strncmpeq(const char *nameValue, const char *name, size_t nameLen) 142 { 143 if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=') 144 return (true); 145 146 return (false); 147 } 148 149 150 /* 151 * Using environment, returns pointer to value associated with name, if any, 152 * else NULL. If the onlyActive flag is set to true, only variables that are 153 * active are returned else all are. 154 */ 155 static inline char * 156 __findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive) 157 { 158 int ndx; 159 160 /* 161 * Find environment variable from end of array (more likely to be 162 * active). A variable created by putenv is always active or it is not 163 * tracked in the array. 164 */ 165 for (ndx = *envNdx; ndx >= 0; ndx--) 166 if (envVars[ndx].putenv) { 167 if (strncmpeq(envVars[ndx].name, name, nameLen)) { 168 *envNdx = ndx; 169 return (envVars[ndx].name + nameLen + 170 sizeof ("=") - 1); 171 } 172 } else if ((!onlyActive || envVars[ndx].active) && 173 (envVars[ndx].nameLen == nameLen && 174 strncmpeq(envVars[ndx].name, name, nameLen))) { 175 *envNdx = ndx; 176 return (envVars[ndx].value); 177 } 178 179 return (NULL); 180 } 181 182 183 /* 184 * Using environ, returns pointer to value associated with name, if any, else 185 * NULL. Used on the original environ passed into the program. 186 */ 187 static char * 188 __findenv_environ(const char *name, size_t nameLen) 189 { 190 int envNdx; 191 192 /* Find variable within environ. */ 193 for (envNdx = 0; environ[envNdx] != NULL; envNdx++) 194 if (strncmpeq(environ[envNdx], name, nameLen)) 195 return (&(environ[envNdx][nameLen + sizeof("=") - 1])); 196 197 return (NULL); 198 } 199 200 201 /* 202 * Remove variable added by putenv() from variable tracking array. 203 */ 204 static void 205 __remove_putenv(int envNdx) 206 { 207 envVarsTotal--; 208 if (envVarsTotal > envNdx) 209 memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]), 210 (envVarsTotal - envNdx) * sizeof (*envVars)); 211 memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars)); 212 213 return; 214 } 215 216 217 /* 218 * Deallocate the environment built from environ as well as environ then set 219 * both to NULL. Eases debugging of memory leaks. 220 */ 221 static void 222 __clean_env(bool freeVars) 223 { 224 int envNdx; 225 226 /* Deallocate environment and environ if created by *env(). */ 227 if (envVars != NULL) { 228 for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) 229 /* Free variables or deactivate them. */ 230 if (envVars[envNdx].putenv) { 231 if (!freeVars) 232 __remove_putenv(envNdx); 233 } else { 234 if (freeVars) 235 free(envVars[envNdx].name); 236 else 237 envVars[envNdx].active = false; 238 } 239 if (freeVars) { 240 free(envVars); 241 envVars = NULL; 242 } else 243 envActive = 0; 244 245 /* Restore original environ if it has not updated by program. */ 246 if (origEnviron != NULL) { 247 if (environ == intEnviron) 248 environ = origEnviron; 249 free(intEnviron); 250 intEnviron = NULL; 251 environSize = 0; 252 } 253 } 254 255 return; 256 } 257 258 259 /* 260 * Using the environment, rebuild the environ array for use by other C library 261 * calls that depend upon it. 262 */ 263 static int 264 __rebuild_environ(int newEnvironSize) 265 { 266 char **tmpEnviron; 267 int envNdx; 268 int environNdx; 269 int tmpEnvironSize; 270 271 /* Resize environ. */ 272 if (newEnvironSize > environSize) { 273 tmpEnvironSize = newEnvironSize * 2; 274 tmpEnviron = realloc(intEnviron, sizeof (*intEnviron) * 275 (tmpEnvironSize + 1)); 276 if (tmpEnviron == NULL) 277 return (-1); 278 environSize = tmpEnvironSize; 279 intEnviron = tmpEnviron; 280 } 281 envActive = newEnvironSize; 282 283 /* Assign active variables to environ. */ 284 for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--) 285 if (envVars[envNdx].active) 286 intEnviron[environNdx++] = envVars[envNdx].name; 287 intEnviron[environNdx] = NULL; 288 289 /* Always set environ which may have been replaced by program. */ 290 environ = intEnviron; 291 292 return (0); 293 } 294 295 296 /* 297 * Enlarge new environment. 298 */ 299 static inline bool 300 __enlarge_env(void) 301 { 302 int newEnvVarsSize; 303 struct envVars *tmpEnvVars; 304 305 envVarsTotal++; 306 if (envVarsTotal > envVarsSize) { 307 newEnvVarsSize = envVarsTotal * 2; 308 tmpEnvVars = realloc(envVars, sizeof (*envVars) * 309 newEnvVarsSize); 310 if (tmpEnvVars == NULL) { 311 envVarsTotal--; 312 return (false); 313 } 314 envVarsSize = newEnvVarsSize; 315 envVars = tmpEnvVars; 316 } 317 318 return (true); 319 } 320 321 322 /* 323 * Using environ, build an environment for use by standard C library calls. 324 */ 325 static int 326 __build_env(void) 327 { 328 char **env; 329 int activeNdx; 330 int envNdx; 331 int savedErrno; 332 size_t nameLen; 333 334 /* Check for non-existant environment. */ 335 if (environ == NULL || environ[0] == NULL) 336 return (0); 337 338 /* Count environment variables. */ 339 for (env = environ, envVarsTotal = 0; *env != NULL; env++) 340 envVarsTotal++; 341 envVarsSize = envVarsTotal * 2; 342 343 /* Create new environment. */ 344 envVars = calloc(1, sizeof (*envVars) * envVarsSize); 345 if (envVars == NULL) 346 goto Failure; 347 348 /* Copy environ values and keep track of them. */ 349 for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) { 350 envVars[envNdx].putenv = false; 351 envVars[envNdx].name = 352 strdup(environ[envVarsTotal - envNdx - 1]); 353 if (envVars[envNdx].name == NULL) 354 goto Failure; 355 envVars[envNdx].value = strchr(envVars[envNdx].name, '='); 356 if (envVars[envNdx].value != NULL) { 357 envVars[envNdx].value++; 358 envVars[envNdx].valueSize = 359 strlen(envVars[envNdx].value); 360 } else { 361 __env_warnx(CorruptEnvValueMsg, envVars[envNdx].name, 362 strlen(envVars[envNdx].name)); 363 errno = EFAULT; 364 goto Failure; 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 errno = EFAULT; 380 goto Failure; 381 } 382 envVars[activeNdx].active = true; 383 } 384 385 /* Create a new environ. */ 386 origEnviron = environ; 387 environ = NULL; 388 if (__rebuild_environ(envVarsTotal) == 0) 389 return (0); 390 391 Failure: 392 savedErrno = errno; 393 __clean_env(true); 394 errno = savedErrno; 395 396 return (-1); 397 } 398 399 400 /* 401 * Destructor function with default argument to __clean_env(). 402 */ 403 static void 404 __clean_env_destructor(void) 405 { 406 __clean_env(true); 407 408 return; 409 } 410 411 412 /* 413 * Returns the value of a variable or NULL if none are found. 414 */ 415 char * 416 getenv(const char *name) 417 { 418 int envNdx; 419 size_t nameLen; 420 421 /* Check for malformed name. */ 422 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 423 errno = EINVAL; 424 return (NULL); 425 } 426 427 /* 428 * An empty environment (environ or its first value) regardless if 429 * environ has been copied before will return a NULL. 430 * 431 * If the environment is not empty, find an environment variable via 432 * environ if environ has not been copied via an *env() call or been 433 * replaced by a running program, otherwise, use the rebuilt 434 * environment. 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 = 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 */ 532 static 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 errno = EFAULT; 562 return (-1); 563 } 564 if (__setenv(*env, equals - *env, equals + 1, 565 1) == -1) 566 return (-1); 567 } 568 } 569 570 return (0); 571 } 572 573 574 /* 575 * The exposed setenv() that peforms a few tests before calling the function 576 * (__setenv()) that does the actual work of inserting a variable into the 577 * environment. 578 */ 579 int 580 setenv(const char *name, const char *value, int overwrite) 581 { 582 size_t nameLen; 583 584 /* Check for malformed name. */ 585 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 586 errno = EINVAL; 587 return (-1); 588 } 589 590 /* Initialize environment. */ 591 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 592 return (-1); 593 594 return (__setenv(name, nameLen, value, overwrite)); 595 } 596 597 598 /* 599 * Insert a "name=value" string into the environment. Special settings must be 600 * made to keep setenv() from reusing this memory block and unsetenv() from 601 * allowing it to be tracked. 602 */ 603 int 604 putenv(char *string) 605 { 606 char *equals; 607 int envNdx; 608 int newEnvActive; 609 size_t nameLen; 610 611 /* Check for malformed argument. */ 612 if (string == NULL || (equals = strchr(string, '=')) == NULL || 613 (nameLen = equals - string) == 0) { 614 errno = EINVAL; 615 return (-1); 616 } 617 618 /* Initialize environment. */ 619 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 620 return (-1); 621 622 /* Deactivate previous environment variable. */ 623 envNdx = envVarsTotal - 1; 624 newEnvActive = envActive; 625 if (__findenv(string, nameLen, &envNdx, true) != NULL) { 626 /* Reuse previous putenv slot. */ 627 if (envVars[envNdx].putenv) { 628 envVars[envNdx].name = string; 629 return (__rebuild_environ(envActive)); 630 } else { 631 newEnvActive--; 632 envVars[envNdx].active = false; 633 } 634 } 635 636 /* Enlarge environment. */ 637 envNdx = envVarsTotal; 638 if (!__enlarge_env()) 639 return (-1); 640 641 /* Create environment entry. */ 642 envVars[envNdx].name = string; 643 envVars[envNdx].nameLen = -1; 644 envVars[envNdx].value = NULL; 645 envVars[envNdx].valueSize = -1; 646 envVars[envNdx].putenv = true; 647 envVars[envNdx].active = true; 648 newEnvActive++; 649 650 return (__rebuild_environ(newEnvActive)); 651 } 652 653 654 /* 655 * Unset variable with the same name by flagging it as inactive. No variable is 656 * ever freed. 657 */ 658 int 659 unsetenv(const char *name) 660 { 661 int envNdx; 662 size_t nameLen; 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 envNdx = envVarsTotal - 1; 676 if (__findenv(name, nameLen, &envNdx, true) != NULL) { 677 envVars[envNdx].active = false; 678 if (envVars[envNdx].putenv) 679 __remove_putenv(envNdx); 680 __rebuild_environ(envActive - 1); 681 } 682 683 return (0); 684 } 685