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 --- 19 unchanged lines hidden (view full) --- 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 171525 2007-07-20 23:30:13Z scf $"); |
37 38 |
39static const char CorruptEnvFindMsg[] = 40 "environment corrupt; unable to find %.*s"; 41static 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 */ 55extern char **environ; 56static char **origEnviron; |
57static char **intEnviron = NULL; |
58static 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 --- 22 unchanged lines hidden (view full) --- 88 * envVarsTotal: Number of total variables in array (active or not). 89 */ 90static int envActive = 0; 91static int envVarsSize = 0; 92static int envVarsTotal = 0; 93 94 95/* Deinitialization of new environment. */ |
96static 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 */ 103static inline size_t 104__strleneq(const char *str) --- 72 unchanged lines hidden (view full) --- 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 */ 187static 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 */ 204static 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 */ 246static 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 */ 282static inline bool --- 23 unchanged lines hidden (view full) --- 306 * Using environ, build an environment for use by standard C library calls. 307 */ 308static 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); --- 8 unchanged lines hidden (view full) --- 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, nameLen, envVars[envNdx].name); |
360 errno = EFAULT; 361 goto Failure; 362 } 363 envVars[activeNdx].active = true; 364 } 365 366 /* Create a new environ. */ |
367 origEnviron = environ; 368 environ = NULL; |
369 if (__rebuild_environ(envVarsTotal) == 0) 370 return (0); |
371 |
372Failure: 373 savedErrno = errno; |
374 __clean_env(true); |
375 errno = savedErrno; 376 377 return (-1); 378} 379 380 381/* |
382 * Destructor function with default argument to __clean_env(). |
383 */ 384static void |
385__clean_env_destructor(void) |
386{ |
387 __clean_env(true); |
388 389 return; 390} 391 392 393/* |
394 * Returns the value of a variable or NULL if none are found. 395 */ 396char * 397getenv(const char *name) 398{ 399 int envNdx; 400 size_t nameLen; 401 402 /* Check for malformed name. */ 403 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 404 errno = EINVAL; 405 return (NULL); 406 } 407 |
408 /* 409 * Find environment variable via environ if no changes have been made 410 * via a *env() call or environ has been replaced by a running program, 411 * otherwise, use the rebuilt environment. 412 */ 413 if (envVars == NULL || environ != intEnviron) |
414 return (__findenv_environ(name, nameLen)); 415 else { 416 envNdx = envVarsTotal - 1; 417 return (__findenv(name, nameLen, &envNdx, true)); 418 } 419} 420 421 422/* 423 * Set the value of a variable. Older settings are labeled as inactive. If an 424 * older setting has enough room to store the new value, it will be reused. No 425 * previous variables are ever freed here to avoid causing a segmentation fault 426 * in a user's code. |
427 * 428 * The variables nameLen and valueLen are passed into here to allow the caller 429 * to calculate the length by means besides just strlen(). |
430 */ |
431static int 432__setenv(const char *name, size_t nameLen, const char *value, int overwrite) |
433{ 434 bool reuse; 435 char *env; 436 int envNdx; 437 int newEnvActive; |
438 size_t valueLen; 439 |
440 /* Find existing environment variable large enough to use. */ 441 envNdx = envVarsTotal - 1; 442 newEnvActive = envActive; 443 valueLen = strlen(value); 444 reuse = false; 445 if (__findenv(name, nameLen, &envNdx, false) != NULL) { 446 /* Deactivate entry if overwrite is allowed. */ 447 if (envVars[envNdx].active) { --- 5 unchanged lines hidden (view full) --- 453 454 /* putenv() created variable cannot be reused. */ 455 if (envVars[envNdx].putenv) 456 __remove_putenv(envNdx); 457 458 /* Entry is large enough to reuse. */ 459 else if (envVars[envNdx].valueSize >= valueLen) 460 reuse = true; |
461 } 462 463 /* Create new variable if none was found of sufficient size. */ 464 if (! reuse) { 465 /* Enlarge environment. */ 466 envNdx = envVarsTotal; 467 if (!__enlarge_env()) 468 return (-1); --- 26 unchanged lines hidden (view full) --- 495 if (reuse) 496 return (0); 497 else 498 return (__rebuild_environ(newEnvActive)); 499} 500 501 502/* |
503 * If the program attempts to replace the array of environment variables 504 * (environ) environ, then deactivate all variables and merge in the new list 505 * from environ. 506 */ 507static int 508__merge_environ(void) 509{ 510 char **env; 511 char *equals; 512 513 /* environ has been replaced. clean up everything. */ 514 if (envVarsTotal > 0 && environ != intEnviron) { 515 /* Deactivate all environment variables. */ 516 if (envActive > 0) { 517 origEnviron = NULL; 518 __clean_env(false); 519 } 520 521 /* 522 * Insert new environ into existing, yet deactivated, 523 * environment array. 524 */ 525 origEnviron = environ; 526 if (origEnviron != NULL) 527 for (env = origEnviron; *env != NULL; env++) { 528 if ((equals = strchr(*env, '=')) == NULL) { 529 warnx(CorruptEnvValueMsg, *env); 530 errno = EFAULT; 531 return (-1); 532 } 533 if (__setenv(*env, equals - *env, equals + 1, 534 1) == -1) 535 return (-1); 536 } 537 } 538 539 return (0); 540} 541 542 543/* 544 * The exposed setenv() that peforms a few tests before calling the function 545 * (__setenv()) that does the actual work of inserting a variable into the 546 * environment. 547 */ 548int 549setenv(const char *name, const char *value, int overwrite) 550{ 551 size_t nameLen; 552 553 /* Check for malformed name. */ 554 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 555 errno = EINVAL; 556 return (-1); 557 } 558 559 /* Initialize environment. */ 560 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) 561 return (-1); 562 563 return (__setenv(name, nameLen, value, overwrite)); 564} 565 566 567/* 568 * Insert a "name=value" string into the environment. Special settings must be |
569 * made to keep setenv() from reusing this memory block and unsetenv() from 570 * allowing it to be tracked. 571 */ 572int 573putenv(char *string) 574{ 575 char *equals; 576 int envNdx; 577 int newEnvActive; 578 size_t nameLen; 579 580 /* Check for malformed argument. */ 581 if (string == NULL || (equals = strchr(string, '=')) == NULL || 582 (nameLen = equals - string) == 0) { 583 errno = EINVAL; 584 return (-1); 585 } 586 587 /* Initialize environment. */ |
588 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) |
589 return (-1); 590 591 /* Deactivate previous environment variable. */ 592 envNdx = envVarsTotal - 1; 593 newEnvActive = envActive; 594 if (__findenv(string, nameLen, &envNdx, true) != NULL) { 595 /* Reuse previous putenv slot. */ 596 if (envVars[envNdx].putenv) { --- 35 unchanged lines hidden (view full) --- 632 633 /* Check for malformed name. */ 634 if (name == NULL || (nameLen = __strleneq(name)) == 0) { 635 errno = EINVAL; 636 return (-1); 637 } 638 639 /* Initialize environment. */ |
640 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) |
641 return (-1); 642 643 /* Deactivate specified variable. */ 644 envNdx = envVarsTotal - 1; 645 if (__findenv(name, nameLen, &envNdx, true) != NULL) { 646 envVars[envNdx].active = false; 647 if (envVars[envNdx].putenv) 648 __remove_putenv(envNdx); 649 __rebuild_environ(envActive - 1); 650 } 651 652 return (0); 653} |