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$"); 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 42 static const char CorruptEnvFindMsg[] = "environment corrupt; unable to find "; 43 static 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 */ 57 extern char **environ; 58 static char **origEnviron; 59 static char **intEnviron = NULL; 60 static 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 */ 76 static 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 */ 92 static int envActive = 0; 93 static int envVarsSize = 0; 94 static int envVarsTotal = 0; 95 96 97 /* Deinitialization of new environment. */ 98 static 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 */ 105 static 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 */ 125 static 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 */ 141 static inline bool 142 strncmpeq(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 */ 156 static 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 */ 188 static 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 */ 205 static 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 */ 222 static 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 */ 264 static 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 = reallocarray(intEnviron, tmpEnvironSize + 1, 276 sizeof(*intEnviron)); 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 */ 300 static 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 = reallocarray(envVars, newEnvVarsSize, 310 sizeof(*envVars)); 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 */ 326 static 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(envVarsSize, sizeof(*envVars)); 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 errno = EFAULT; 365 goto Failure; 366 } 367 368 /* 369 * Find most current version of variable to make active. This 370 * will prevent multiple active variables from being created 371 * during this initialization phase. 372 */ 373 nameLen = envVars[envNdx].value - envVars[envNdx].name - 1; 374 envVars[envNdx].nameLen = nameLen; 375 activeNdx = envVarsTotal - 1; 376 if (__findenv(envVars[envNdx].name, nameLen, &activeNdx, 377 false) == NULL) { 378 __env_warnx(CorruptEnvFindMsg, envVars[envNdx].name, 379 nameLen); 380 errno = EFAULT; 381 goto Failure; 382 } 383 envVars[activeNdx].active = true; 384 } 385 386 /* Create a new environ. */ 387 origEnviron = environ; 388 environ = NULL; 389 if (__rebuild_environ(envVarsTotal) == 0) 390 return (0); 391 392 Failure: 393 savedErrno = errno; 394 __clean_env(true); 395 errno = savedErrno; 396 397 return (-1); 398 } 399 400 401 /* 402 * Destructor function with default argument to __clean_env(). 403 */ 404 static void 405 __clean_env_destructor(void) 406 { 407 __clean_env(true); 408 409 return; 410 } 411 412 413 /* 414 * Returns the value of a variable or NULL if none are found. 415 */ 416 char * 417 getenv(const char *name) 418 { 419 int envNdx; 420 size_t nameLen; 421 422 /* Check for malformed name. */ 423 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 424 errno = EINVAL; 425 return (NULL); 426 } 427 428 /* 429 * Variable search order: 430 * 1. Check for an empty environ. This allows an application to clear 431 * the environment. 432 * 2. Search the external environ array. 433 * 3. Search the internal environment. 434 * 435 * Since malloc() depends upon getenv(), getenv() must never cause the 436 * internal environment storage to be generated. 437 */ 438 if (environ == NULL || environ[0] == NULL) 439 return (NULL); 440 else if (envVars == NULL || environ != intEnviron) 441 return (__findenv_environ(name, nameLen)); 442 else { 443 envNdx = envVarsTotal - 1; 444 return (__findenv(name, nameLen, &envNdx, true)); 445 } 446 } 447 448 449 /* 450 * Set the value of a variable. Older settings are labeled as inactive. If an 451 * older setting has enough room to store the new value, it will be reused. No 452 * previous variables are ever freed here to avoid causing a segmentation fault 453 * in a user's code. 454 * 455 * The variables nameLen and valueLen are passed into here to allow the caller 456 * to calculate the length by means besides just strlen(). 457 */ 458 static int 459 __setenv(const char *name, size_t nameLen, const char *value, int overwrite) 460 { 461 bool reuse; 462 char *env; 463 int envNdx; 464 int newEnvActive; 465 size_t valueLen; 466 467 /* Find existing environment variable large enough to use. */ 468 envNdx = envVarsTotal - 1; 469 newEnvActive = envActive; 470 valueLen = strlen(value); 471 reuse = false; 472 if (__findenv(name, nameLen, &envNdx, false) != NULL) { 473 /* Deactivate entry if overwrite is allowed. */ 474 if (envVars[envNdx].active) { 475 if (overwrite == 0) 476 return (0); 477 envVars[envNdx].active = false; 478 newEnvActive--; 479 } 480 481 /* putenv() created variable cannot be reused. */ 482 if (envVars[envNdx].putenv) 483 __remove_putenv(envNdx); 484 485 /* Entry is large enough to reuse. */ 486 else if (envVars[envNdx].valueSize >= valueLen) 487 reuse = true; 488 } 489 490 /* Create new variable if none was found of sufficient size. */ 491 if (! reuse) { 492 /* Enlarge environment. */ 493 envNdx = envVarsTotal; 494 if (!__enlarge_env()) 495 return (-1); 496 497 /* Create environment entry. */ 498 envVars[envNdx].name = malloc(nameLen + sizeof ("=") + 499 valueLen); 500 if (envVars[envNdx].name == NULL) { 501 envVarsTotal--; 502 return (-1); 503 } 504 envVars[envNdx].nameLen = nameLen; 505 envVars[envNdx].valueSize = valueLen; 506 507 /* Save name of name/value pair. */ 508 env = stpncpy(envVars[envNdx].name, name, nameLen); 509 *env++ = '='; 510 } 511 else 512 env = envVars[envNdx].value; 513 514 /* Save value of name/value pair. */ 515 strcpy(env, value); 516 envVars[envNdx].value = env; 517 envVars[envNdx].active = true; 518 newEnvActive++; 519 520 /* No need to rebuild environ if an active variable was reused. */ 521 if (reuse && newEnvActive == envActive) 522 return (0); 523 else 524 return (__rebuild_environ(newEnvActive)); 525 } 526 527 528 /* 529 * If the program attempts to replace the array of environment variables 530 * (environ) environ or sets the first varible to NULL, then deactivate all 531 * variables and merge in the new list from environ. 532 */ 533 static int 534 __merge_environ(void) 535 { 536 char **env; 537 char *equals; 538 539 /* 540 * Internally-built environ has been replaced or cleared (detected by 541 * using the count of active variables against a NULL as the first value 542 * in environ). Clean up everything. 543 */ 544 if (intEnviron != NULL && (environ != intEnviron || (envActive > 0 && 545 environ[0] == NULL))) { 546 /* Deactivate all environment variables. */ 547 if (envActive > 0) { 548 origEnviron = NULL; 549 __clean_env(false); 550 } 551 552 /* 553 * Insert new environ into existing, yet deactivated, 554 * environment array. 555 */ 556 origEnviron = environ; 557 if (origEnviron != NULL) 558 for (env = origEnviron; *env != NULL; env++) { 559 if ((equals = strchr(*env, '=')) == NULL) { 560 __env_warnx(CorruptEnvValueMsg, *env, 561 strlen(*env)); 562 errno = EFAULT; 563 return (-1); 564 } 565 if (__setenv(*env, equals - *env, equals + 1, 566 1) == -1) 567 return (-1); 568 } 569 } 570 571 return (0); 572 } 573 574 575 /* 576 * The exposed setenv() that peforms a few tests before calling the function 577 * (__setenv()) that does the actual work of inserting a variable into the 578 * environment. 579 */ 580 int 581 setenv(const char *name, const char *value, int overwrite) 582 { 583 size_t nameLen; 584 585 /* Check for malformed name. */ 586 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 587 errno = EINVAL; 588 return (-1); 589 } 590 591 /* Initialize environment. */ 592 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 593 return (-1); 594 595 return (__setenv(name, nameLen, value, overwrite)); 596 } 597 598 599 /* 600 * Insert a "name=value" string into the environment. Special settings must be 601 * made to keep setenv() from reusing this memory block and unsetenv() from 602 * allowing it to be tracked. 603 */ 604 int 605 putenv(char *string) 606 { 607 char *equals; 608 int envNdx; 609 int newEnvActive; 610 size_t nameLen; 611 612 /* Check for malformed argument. */ 613 if (string == NULL || (equals = strchr(string, '=')) == NULL || 614 (nameLen = equals - string) == 0) { 615 errno = EINVAL; 616 return (-1); 617 } 618 619 /* Initialize environment. */ 620 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 621 return (-1); 622 623 /* Deactivate previous environment variable. */ 624 envNdx = envVarsTotal - 1; 625 newEnvActive = envActive; 626 if (__findenv(string, nameLen, &envNdx, true) != NULL) { 627 /* Reuse previous putenv slot. */ 628 if (envVars[envNdx].putenv) { 629 envVars[envNdx].name = string; 630 return (__rebuild_environ(envActive)); 631 } else { 632 newEnvActive--; 633 envVars[envNdx].active = false; 634 } 635 } 636 637 /* Enlarge environment. */ 638 envNdx = envVarsTotal; 639 if (!__enlarge_env()) 640 return (-1); 641 642 /* Create environment entry. */ 643 envVars[envNdx].name = string; 644 envVars[envNdx].nameLen = -1; 645 envVars[envNdx].value = NULL; 646 envVars[envNdx].valueSize = -1; 647 envVars[envNdx].putenv = true; 648 envVars[envNdx].active = true; 649 newEnvActive++; 650 651 return (__rebuild_environ(newEnvActive)); 652 } 653 654 655 /* 656 * Unset variable with the same name by flagging it as inactive. No variable is 657 * ever freed. 658 */ 659 int 660 unsetenv(const char *name) 661 { 662 int envNdx; 663 size_t nameLen; 664 int newEnvActive; 665 666 /* Check for malformed name. */ 667 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 668 errno = EINVAL; 669 return (-1); 670 } 671 672 /* Initialize environment. */ 673 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 674 return (-1); 675 676 /* Deactivate specified variable. */ 677 /* Remove all occurrences. */ 678 envNdx = envVarsTotal - 1; 679 newEnvActive = envActive; 680 while (__findenv(name, nameLen, &envNdx, true) != NULL) { 681 envVars[envNdx].active = false; 682 if (envVars[envNdx].putenv) 683 __remove_putenv(envNdx); 684 envNdx--; 685 newEnvActive--; 686 } 687 if (newEnvActive != envActive) 688 __rebuild_environ(newEnvActive); 689 690 return (0); 691 } 692