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