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$"); 37 38 39 static const char CorruptEnvFindMsg[] = 40 "environment corrupt; unable to find %.*s"; 41 static 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 */ 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 * Inline strlen() for performance. Also, perform check for an equals sign. 101 * Cheaper here than peforming a strchr() later. 102 */ 103 static 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 */ 119 static inline bool 120 strncmpeq(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 */ 134 static 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 */ 166 static 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 */ 187 static 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 */ 204 static 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 */ 246 static 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 */ 282 static 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 */ 308 static 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, (int)nameLen, 360 envVars[envNdx].name); 361 errno = EFAULT; 362 goto Failure; 363 } 364 envVars[activeNdx].active = true; 365 } 366 367 /* Create a new environ. */ 368 origEnviron = environ; 369 environ = NULL; 370 if (__rebuild_environ(envVarsTotal) == 0) 371 return (0); 372 373 Failure: 374 savedErrno = errno; 375 __clean_env(true); 376 errno = savedErrno; 377 378 return (-1); 379 } 380 381 382 /* 383 * Destructor function with default argument to __clean_env(). 384 */ 385 static void 386 __clean_env_destructor(void) 387 { 388 __clean_env(true); 389 390 return; 391 } 392 393 394 /* 395 * Returns the value of a variable or NULL if none are found. 396 */ 397 char * 398 getenv(const char *name) 399 { 400 int envNdx; 401 size_t nameLen; 402 403 /* Check for malformed name. */ 404 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 405 errno = EINVAL; 406 return (NULL); 407 } 408 409 /* 410 * Find environment variable via environ if no changes have been made 411 * via a *env() call or environ has been replaced by a running program, 412 * otherwise, use the rebuilt environment. 413 */ 414 if (envVars == NULL || environ != intEnviron) 415 return (__findenv_environ(name, nameLen)); 416 else { 417 envNdx = envVarsTotal - 1; 418 return (__findenv(name, nameLen, &envNdx, true)); 419 } 420 } 421 422 423 /* 424 * Set the value of a variable. Older settings are labeled as inactive. If an 425 * older setting has enough room to store the new value, it will be reused. No 426 * previous variables are ever freed here to avoid causing a segmentation fault 427 * in a user's code. 428 * 429 * The variables nameLen and valueLen are passed into here to allow the caller 430 * to calculate the length by means besides just strlen(). 431 */ 432 static int 433 __setenv(const char *name, size_t nameLen, const char *value, int overwrite) 434 { 435 bool reuse; 436 char *env; 437 int envNdx; 438 int newEnvActive; 439 size_t valueLen; 440 441 /* Find existing environment variable large enough to use. */ 442 envNdx = envVarsTotal - 1; 443 newEnvActive = envActive; 444 valueLen = strlen(value); 445 reuse = false; 446 if (__findenv(name, nameLen, &envNdx, false) != NULL) { 447 /* Deactivate entry if overwrite is allowed. */ 448 if (envVars[envNdx].active) { 449 if (overwrite == 0) 450 return (0); 451 envVars[envNdx].active = false; 452 newEnvActive--; 453 } 454 455 /* putenv() created variable cannot be reused. */ 456 if (envVars[envNdx].putenv) 457 __remove_putenv(envNdx); 458 459 /* Entry is large enough to reuse. */ 460 else if (envVars[envNdx].valueSize >= valueLen) 461 reuse = true; 462 } 463 464 /* Create new variable if none was found of sufficient size. */ 465 if (! reuse) { 466 /* Enlarge environment. */ 467 envNdx = envVarsTotal; 468 if (!__enlarge_env()) 469 return (-1); 470 471 /* Create environment entry. */ 472 envVars[envNdx].name = malloc(nameLen + sizeof ("=") + 473 valueLen); 474 if (envVars[envNdx].name == NULL) { 475 envVarsTotal--; 476 return (-1); 477 } 478 envVars[envNdx].nameLen = nameLen; 479 envVars[envNdx].valueSize = valueLen; 480 481 /* Save name of name/value pair. */ 482 env = stpcpy(envVars[envNdx].name, name); 483 if ((envVars[envNdx].name)[nameLen] != '=') 484 env = stpcpy(env, "="); 485 } 486 else 487 env = envVars[envNdx].value; 488 489 /* Save value of name/value pair. */ 490 strcpy(env, value); 491 envVars[envNdx].value = env; 492 envVars[envNdx].active = true; 493 newEnvActive++; 494 495 /* No need to rebuild environ if an active variable was reused. */ 496 if (reuse && newEnvActive == envActive) 497 return (0); 498 else 499 return (__rebuild_environ(newEnvActive)); 500 } 501 502 503 /* 504 * If the program attempts to replace the array of environment variables 505 * (environ) environ, then deactivate all variables and merge in the new list 506 * from environ. 507 */ 508 static int 509 __merge_environ(void) 510 { 511 char **env; 512 char *equals; 513 514 /* environ has been replaced. clean up everything. */ 515 if (envVarsTotal > 0 && environ != intEnviron) { 516 /* Deactivate all environment variables. */ 517 if (envActive > 0) { 518 origEnviron = NULL; 519 __clean_env(false); 520 } 521 522 /* 523 * Insert new environ into existing, yet deactivated, 524 * environment array. 525 */ 526 origEnviron = environ; 527 if (origEnviron != NULL) 528 for (env = origEnviron; *env != NULL; env++) { 529 if ((equals = strchr(*env, '=')) == NULL) { 530 warnx(CorruptEnvValueMsg, *env); 531 errno = EFAULT; 532 return (-1); 533 } 534 if (__setenv(*env, equals - *env, equals + 1, 535 1) == -1) 536 return (-1); 537 } 538 } 539 540 return (0); 541 } 542 543 544 /* 545 * The exposed setenv() that peforms a few tests before calling the function 546 * (__setenv()) that does the actual work of inserting a variable into the 547 * environment. 548 */ 549 int 550 setenv(const char *name, const char *value, int overwrite) 551 { 552 size_t nameLen; 553 554 /* Check for malformed name. */ 555 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 556 errno = EINVAL; 557 return (-1); 558 } 559 560 /* Initialize environment. */ 561 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 562 return (-1); 563 564 return (__setenv(name, nameLen, value, overwrite)); 565 } 566 567 568 /* 569 * Insert a "name=value" string into the environment. Special settings must be 570 * made to keep setenv() from reusing this memory block and unsetenv() from 571 * allowing it to be tracked. 572 */ 573 int 574 putenv(char *string) 575 { 576 char *equals; 577 int envNdx; 578 int newEnvActive; 579 size_t nameLen; 580 581 /* Check for malformed argument. */ 582 if (string == NULL || (equals = strchr(string, '=')) == NULL || 583 (nameLen = equals - string) == 0) { 584 errno = EINVAL; 585 return (-1); 586 } 587 588 /* Initialize environment. */ 589 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 590 return (-1); 591 592 /* Deactivate previous environment variable. */ 593 envNdx = envVarsTotal - 1; 594 newEnvActive = envActive; 595 if (__findenv(string, nameLen, &envNdx, true) != NULL) { 596 /* Reuse previous putenv slot. */ 597 if (envVars[envNdx].putenv) { 598 envVars[envNdx].name = string; 599 return (__rebuild_environ(envActive)); 600 } else { 601 newEnvActive--; 602 envVars[envNdx].active = false; 603 } 604 } 605 606 /* Enlarge environment. */ 607 envNdx = envVarsTotal; 608 if (!__enlarge_env()) 609 return (-1); 610 611 /* Create environment entry. */ 612 envVars[envNdx].name = string; 613 envVars[envNdx].nameLen = -1; 614 envVars[envNdx].value = NULL; 615 envVars[envNdx].valueSize = -1; 616 envVars[envNdx].putenv = true; 617 envVars[envNdx].active = true; 618 newEnvActive++; 619 620 return (__rebuild_environ(newEnvActive)); 621 } 622 623 624 /* 625 * Unset variable with the same name by flagging it as inactive. No variable is 626 * ever freed. 627 */ 628 int 629 unsetenv(const char *name) 630 { 631 int envNdx; 632 size_t nameLen; 633 634 /* Check for malformed name. */ 635 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 636 errno = EINVAL; 637 return (-1); 638 } 639 640 /* Initialize environment. */ 641 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 642 return (-1); 643 644 /* Deactivate specified variable. */ 645 envNdx = envVarsTotal - 1; 646 if (__findenv(name, nameLen, &envNdx, true) != NULL) { 647 envVars[envNdx].active = false; 648 if (envVars[envNdx].putenv) 649 __remove_putenv(envNdx); 650 __rebuild_environ(envActive - 1); 651 } 652 653 return (0); 654 } 655