Deleted Added
full compact
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 171195 2007-07-04 00:00:41Z scf $");
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. */
87static void __attribute__ ((destructor)) __clean_env(void);
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;
190 tmpEnviron = realloc(environ, sizeof (*environ) *
257 tmpEnviron = realloc(intEnviron, sizeof (*intEnviron) *
258 (tmpEnvironSize + 1));
259 if (tmpEnviron == NULL)
260 return (-1);
261 environSize = tmpEnvironSize;
195 environ = tmpEnviron;
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)
202 environ[environNdx++] = envVars[envNdx].name;
203 environ[environNdx] = NULL;
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;
244 int rtrnVal;
314 int savedErrno;
315 size_t nameLen;
316
317 /* Check for non-existant environment. */
249 if (environ == NULL)
318 if (environ == NULL || environ[0] == NULL)
319 return (0);
251 if (environ[0] == NULL)
252 goto SaveEnviron;
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 {
277 warnx("environment corrupt; missing value for %s",
278 envVars[envNdx].name);
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) {
293 warnx("environment corrupt; unable to find %.*s",
294 nameLen, envVars[envNdx].name);
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. */
302SaveEnviron:
367 origEnviron = environ;
368 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;
369 if (__rebuild_environ(envVarsTotal) == 0)
370 return (0);
371
315 return (rtrnVal);
316
372Failure:
373 savedErrno = errno;
319 __clean_env();
374 __clean_env(true);
375 errno = savedErrno;
376
377 return (-1);
378}
379
380
381/*
327 * Remove variable added by putenv() from variable tracking array.
382 * Destructor function with default argument to __clean_env().
383 */
384static void
330__remove_putenv(int envNdx)
385__clean_env_destructor(void)
386{
332 memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]),
333 (envVarsTotal - envNdx) * sizeof (*envVars));
334 envVarsTotal--;
387 __clean_env(true);
388
389 return;
390}
391
392
393/*
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/*
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
383 /* Find environment variable via environ or rebuilt environment. */
384 if (envVars == NULL)
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 */
399int
400setenv(const char *name, const char *value, int overwrite)
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;
406 size_t nameLen;
438 size_t valueLen;
439
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
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;
440
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/*
483 * Insert a "name=value" string into then environment. Special settings must be
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. */
503 if (envVars == NULL && __build_env() == -1)
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. */
555 if (envVars == NULL && __build_env() == -1)
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}