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