2 * Copyright (c) 2007-2008 Sean C. Farley <scf@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
26 * $FreeBSD: src/lib/libc/stdlib/getenv.c,v 1.15 2008/08/03 22:47:23 scf Exp $
27 * $DragonFly: src/lib/libc/stdlib/getenv.c,v 1.5 2005/04/28 13:51:55 joerg Exp $
30 #include "namespace.h"
31 #include <sys/types.h>
38 #include "un-namespace.h"
41 static const char CorruptEnvFindMsg
[] = "environment corrupt; unable to find ";
42 static const char CorruptEnvValueMsg
[] =
43 "environment corrupt; missing value for ";
47 * Standard environ. environ variable is exposed to entire process.
49 * origEnviron: Upon cleanup on unloading of library or failure, this
50 * allows environ to return to as it was before.
51 * environSize: Number of variables environ can hold. Can only
53 * intEnviron: Internally-built environ. Exposed via environ during
54 * (re)builds of the environment.
56 extern char **environ
;
57 static char **origEnviron
;
58 static char **intEnviron
= NULL
;
59 static int environSize
= 0;
62 * Array of environment variables built from environ. Each element records:
63 * name: Pointer to name=value string
64 * name length: Length of name not counting '=' character
65 * value: Pointer to value within same string as name
66 * value size: Size (not length) of space for value not counting the
68 * active state: true/false value to signify whether variable is active.
69 * Useful since multiple variables with the same name can
70 * co-exist. At most, one variable can be active at any
72 * putenv: Created from putenv() call. This memory must not be
75 static struct envVars
{
85 * Environment array information.
87 * envActive: Number of active variables in array.
88 * envVarsSize: Size of array.
89 * envVarsTotal: Number of total variables in array (active or not).
91 static int envActive
= 0;
92 static int envVarsSize
= 0;
93 static int envVarsTotal
= 0;
96 /* Deinitialization of new environment. */
97 static void __attribute__ ((destructor
)) __clean_env_destructor(void);
101 * A simple version of warnx() to avoid the bloat of including stdio in static
105 __env_warnx(const char *msg
, const char *name
, size_t nameLen
)
107 static const char nl
[] = "\n";
108 static const char progSep
[] = ": ";
110 _write(STDERR_FILENO
, _getprogname(), strlen(_getprogname()));
111 _write(STDERR_FILENO
, progSep
, sizeof(progSep
) - 1);
112 _write(STDERR_FILENO
, msg
, strlen(msg
));
113 _write(STDERR_FILENO
, name
, nameLen
);
114 _write(STDERR_FILENO
, nl
, sizeof(nl
) - 1);
121 * Inline strlen() for performance. Also, perform check for an equals sign.
122 * Cheaper here than peforming a strchr() later.
125 __strleneq(const char *str
)
129 for (s
= str
; *s
!= '\0'; ++s
)
138 * Comparison of an environment name=value to a name.
141 strncmpeq(const char *nameValue
, const char *name
, size_t nameLen
)
143 if (strncmp(nameValue
, name
, nameLen
) == 0 && nameValue
[nameLen
] == '=')
151 * Using environment, returns pointer to value associated with name, if any,
152 * else NULL. If the onlyActive flag is set to true, only variables that are
153 * active are returned else all are.
156 __findenv(const char *name
, size_t nameLen
, int *envNdx
, bool onlyActive
)
161 * Find environment variable from end of array (more likely to be
162 * active). A variable created by putenv is always active or it is not
163 * tracked in the array.
165 for (ndx
= *envNdx
; ndx
>= 0; ndx
--)
166 if (envVars
[ndx
].putenv
) {
167 if (strncmpeq(envVars
[ndx
].name
, name
, nameLen
)) {
169 return (envVars
[ndx
].name
+ nameLen
+
172 } else if ((!onlyActive
|| envVars
[ndx
].active
) &&
173 (envVars
[ndx
].nameLen
== nameLen
&&
174 strncmpeq(envVars
[ndx
].name
, name
, nameLen
))) {
176 return (envVars
[ndx
].value
);
184 * Using environ, returns pointer to value associated with name, if any, else
185 * NULL. Used on the original environ passed into the program.
188 __findenv_environ(const char *name
, size_t nameLen
)
192 /* Find variable within environ. */
193 for (envNdx
= 0; environ
[envNdx
] != NULL
; envNdx
++)
194 if (strncmpeq(environ
[envNdx
], name
, nameLen
))
195 return (&(environ
[envNdx
][nameLen
+ sizeof("=") - 1]));
202 * Remove variable added by putenv() from variable tracking array.
205 __remove_putenv(int envNdx
)
208 if (envVarsTotal
> envNdx
)
209 memmove(&(envVars
[envNdx
]), &(envVars
[envNdx
+ 1]),
210 (envVarsTotal
- envNdx
) * sizeof (*envVars
));
211 memset(&(envVars
[envVarsTotal
]), 0, sizeof (*envVars
));
218 * Deallocate the environment built from environ as well as environ then set
219 * both to NULL. Eases debugging of memory leaks.
222 __clean_env(bool freeVars
)
226 /* Deallocate environment and environ if created by *env(). */
227 if (envVars
!= NULL
) {
228 for (envNdx
= envVarsTotal
- 1; envNdx
>= 0; envNdx
--)
229 /* Free variables or deactivate them. */
230 if (envVars
[envNdx
].putenv
) {
232 __remove_putenv(envNdx
);
235 free(envVars
[envNdx
].name
);
237 envVars
[envNdx
].active
= false;
245 /* Restore original environ if it has not updated by program. */
246 if (origEnviron
!= NULL
) {
247 if (environ
== intEnviron
)
248 environ
= origEnviron
;
260 * Using the environment, rebuild the environ array for use by other C library
261 * calls that depend upon it.
264 __rebuild_environ(int newEnvironSize
)
271 /* Resize environ. */
272 if (newEnvironSize
> environSize
) {
273 tmpEnvironSize
= newEnvironSize
* 2;
274 tmpEnviron
= realloc(intEnviron
, sizeof (*intEnviron
) *
275 (tmpEnvironSize
+ 1));
276 if (tmpEnviron
== NULL
)
278 environSize
= tmpEnvironSize
;
279 intEnviron
= tmpEnviron
;
281 envActive
= newEnvironSize
;
283 /* Assign active variables to environ. */
284 for (envNdx
= envVarsTotal
- 1, environNdx
= 0; envNdx
>= 0; envNdx
--)
285 if (envVars
[envNdx
].active
)
286 intEnviron
[environNdx
++] = envVars
[envNdx
].name
;
287 intEnviron
[environNdx
] = NULL
;
289 /* Always set environ which may have been replaced by program. */
290 environ
= intEnviron
;
297 * Enlarge new environment.
303 struct envVars
*tmpEnvVars
;
306 if (envVarsTotal
> envVarsSize
) {
307 newEnvVarsSize
= envVarsTotal
* 2;
308 tmpEnvVars
= realloc(envVars
, sizeof (*envVars
) *
310 if (tmpEnvVars
== NULL
) {
314 envVarsSize
= newEnvVarsSize
;
315 envVars
= tmpEnvVars
;
323 * Using environ, build an environment for use by standard C library calls.
334 /* Check for non-existant environment. */
335 if (environ
== NULL
|| environ
[0] == NULL
)
338 /* Count environment variables. */
339 for (env
= environ
, envVarsTotal
= 0; *env
!= NULL
; env
++)
341 envVarsSize
= envVarsTotal
* 2;
343 /* Create new environment. */
344 envVars
= calloc(1, sizeof (*envVars
) * envVarsSize
);
348 /* Copy environ values and keep track of them. */
349 for (envNdx
= envVarsTotal
- 1; envNdx
>= 0; envNdx
--) {
350 envVars
[envNdx
].putenv
= false;
351 envVars
[envNdx
].name
=
352 strdup(environ
[envVarsTotal
- envNdx
- 1]);
353 if (envVars
[envNdx
].name
== NULL
)
355 envVars
[envNdx
].value
= strchr(envVars
[envNdx
].name
, '=');
356 if (envVars
[envNdx
].value
!= NULL
) {
357 envVars
[envNdx
].value
++;
358 envVars
[envNdx
].valueSize
=
359 strlen(envVars
[envNdx
].value
);
361 __env_warnx(CorruptEnvValueMsg
, envVars
[envNdx
].name
,
362 strlen(envVars
[envNdx
].name
));
368 * Find most current version of variable to make active. This
369 * will prevent multiple active variables from being created
370 * during this initialization phase.
372 nameLen
= envVars
[envNdx
].value
- envVars
[envNdx
].name
- 1;
373 envVars
[envNdx
].nameLen
= nameLen
;
374 activeNdx
= envVarsTotal
- 1;
375 if (__findenv(envVars
[envNdx
].name
, nameLen
, &activeNdx
,
377 __env_warnx(CorruptEnvFindMsg
, envVars
[envNdx
].name
,
382 envVars
[activeNdx
].active
= true;
385 /* Create a new environ. */
386 origEnviron
= environ
;
388 if (__rebuild_environ(envVarsTotal
) == 0)
401 * Destructor function with default argument to __clean_env().
404 __clean_env_destructor(void)
413 * Returns the value of a variable or NULL if none are found.
416 getenv(const char *name
)
421 /* Check for malformed name. */
422 if (name
== NULL
|| (nameLen
= __strleneq(name
)) == 0) {
428 * An empty environment (environ or its first value) regardless if
429 * environ has been copied before will return a NULL.
431 * If the environment is not empty, find an environment variable via
432 * environ if environ has not been copied via an *env() call or been
433 * replaced by a running program, otherwise, use the rebuilt
436 if (environ
== NULL
|| environ
[0] == NULL
)
438 else if (envVars
== NULL
|| environ
!= intEnviron
)
439 return (__findenv_environ(name
, nameLen
));
441 envNdx
= envVarsTotal
- 1;
442 return (__findenv(name
, nameLen
, &envNdx
, true));
448 * Set the value of a variable. Older settings are labeled as inactive. If an
449 * older setting has enough room to store the new value, it will be reused. No
450 * previous variables are ever freed here to avoid causing a segmentation fault
453 * The variables nameLen and valueLen are passed into here to allow the caller
454 * to calculate the length by means besides just strlen().
457 __setenv(const char *name
, size_t nameLen
, const char *value
, int overwrite
)
465 /* Find existing environment variable large enough to use. */
466 envNdx
= envVarsTotal
- 1;
467 newEnvActive
= envActive
;
468 valueLen
= strlen(value
);
470 if (__findenv(name
, nameLen
, &envNdx
, false) != NULL
) {
471 /* Deactivate entry if overwrite is allowed. */
472 if (envVars
[envNdx
].active
) {
475 envVars
[envNdx
].active
= false;
479 /* putenv() created variable cannot be reused. */
480 if (envVars
[envNdx
].putenv
)
481 __remove_putenv(envNdx
);
483 /* Entry is large enough to reuse. */
484 else if (envVars
[envNdx
].valueSize
>= valueLen
)
488 /* Create new variable if none was found of sufficient size. */
490 /* Enlarge environment. */
491 envNdx
= envVarsTotal
;
492 if (!__enlarge_env())
495 /* Create environment entry. */
496 envVars
[envNdx
].name
= malloc(nameLen
+ sizeof ("=") +
498 if (envVars
[envNdx
].name
== NULL
) {
502 envVars
[envNdx
].nameLen
= nameLen
;
503 envVars
[envNdx
].valueSize
= valueLen
;
505 /* Save name of name/value pair. */
506 env
= stpcpy(envVars
[envNdx
].name
, name
);
507 if ((envVars
[envNdx
].name
)[nameLen
] != '=')
508 env
= stpcpy(env
, "=");
511 env
= envVars
[envNdx
].value
;
513 /* Save value of name/value pair. */
515 envVars
[envNdx
].value
= env
;
516 envVars
[envNdx
].active
= true;
519 /* No need to rebuild environ if an active variable was reused. */
520 if (reuse
&& newEnvActive
== envActive
)
523 return (__rebuild_environ(newEnvActive
));
528 * If the program attempts to replace the array of environment variables
529 * (environ) environ or sets the first varible to NULL, then deactivate all
530 * variables and merge in the new list from environ.
533 __merge_environ(void)
539 * Internally-built environ has been replaced or cleared (detected by
540 * using the count of active variables against a NULL as the first value
541 * in environ). Clean up everything.
543 if (intEnviron
!= NULL
&& (environ
!= intEnviron
|| (envActive
> 0 &&
544 environ
[0] == NULL
))) {
545 /* Deactivate all environment variables. */
552 * Insert new environ into existing, yet deactivated,
555 origEnviron
= environ
;
556 if (origEnviron
!= NULL
)
557 for (env
= origEnviron
; *env
!= NULL
; env
++) {
558 if ((equals
= strchr(*env
, '=')) == NULL
) {
559 __env_warnx(CorruptEnvValueMsg
, *env
,
564 if (__setenv(*env
, equals
- *env
, equals
+ 1,
575 * The exposed setenv() that peforms a few tests before calling the function
576 * (__setenv()) that does the actual work of inserting a variable into the
580 setenv(const char *name
, const char *value
, int overwrite
)
584 /* Check for malformed name. */
585 if (name
== NULL
|| (nameLen
= __strleneq(name
)) == 0) {
590 /* Initialize environment. */
591 if (__merge_environ() == -1 || (envVars
== NULL
&& __build_env() == -1))
594 return (__setenv(name
, nameLen
, value
, overwrite
));
599 * Insert a "name=value" string into the environment. Special settings must be
600 * made to keep setenv() from reusing this memory block and unsetenv() from
601 * allowing it to be tracked.
611 /* Check for malformed argument. */
612 if (string
== NULL
|| (equals
= strchr(string
, '=')) == NULL
||
613 (nameLen
= equals
- string
) == 0) {
618 /* Initialize environment. */
619 if (__merge_environ() == -1 || (envVars
== NULL
&& __build_env() == -1))
622 /* Deactivate previous environment variable. */
623 envNdx
= envVarsTotal
- 1;
624 newEnvActive
= envActive
;
625 if (__findenv(string
, nameLen
, &envNdx
, true) != NULL
) {
626 /* Reuse previous putenv slot. */
627 if (envVars
[envNdx
].putenv
) {
628 envVars
[envNdx
].name
= string
;
629 return (__rebuild_environ(envActive
));
632 envVars
[envNdx
].active
= false;
636 /* Enlarge environment. */
637 envNdx
= envVarsTotal
;
638 if (!__enlarge_env())
641 /* Create environment entry. */
642 envVars
[envNdx
].name
= string
;
643 envVars
[envNdx
].nameLen
= -1;
644 envVars
[envNdx
].value
= NULL
;
645 envVars
[envNdx
].valueSize
= -1;
646 envVars
[envNdx
].putenv
= true;
647 envVars
[envNdx
].active
= true;
650 return (__rebuild_environ(newEnvActive
));
655 * Unset variable with the same name by flagging it as inactive. No variable is
659 unsetenv(const char *name
)
664 /* Check for malformed name. */
665 if (name
== NULL
|| (nameLen
= __strleneq(name
)) == 0) {
670 /* Initialize environment. */
671 if (__merge_environ() == -1 || (envVars
== NULL
&& __build_env() == -1))
674 /* Deactivate specified variable. */
675 envNdx
= envVarsTotal
- 1;
676 if (__findenv(name
, nameLen
, &envNdx
, true) != NULL
) {
677 envVars
[envNdx
].active
= false;
678 if (envVars
[envNdx
].putenv
)
679 __remove_putenv(envNdx
);
680 __rebuild_environ(envActive
- 1);