getenv.c revision 172294
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 172294 2007-09-22 02:30:44Z 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
66 *			nul character
67 *	active state:	true/false value to signify whether variable is active.
68 *			Useful since multiple variables with the same name can
69 *			co-exist.  At most, one variable can be active at any
70 *			one time.
71 *	putenv:		Created from putenv() call.  This memory must not be
72 *			reused.
73 */
74static struct envVars {
75	size_t nameLen;
76	size_t valueSize;
77	char *name;
78	char *value;
79	bool active;
80	bool putenv;
81} *envVars = NULL;
82
83/*
84 * Environment array information.
85 *
86 *	envActive:	Number of active variables in array.
87 *	envVarsSize:	Size of array.
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)
105{
106	const char *s;
107
108	for (s = str; *s != '\0'; ++s)
109		if (*s == '=')
110			return (0);
111
112	return (s - str);
113}
114
115
116/*
117 * Comparison of an environment name=value to a name.
118 */
119static inline bool
120strncmpeq(const char *nameValue, const char *name, size_t nameLen)
121{
122	if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=')
123		return (true);
124
125	return (false);
126}
127
128
129/*
130 * Using environment, returns pointer to value associated with name, if any,
131 * else NULL.  If the onlyActive flag is set to true, only variables that are
132 * active are returned else all are.
133 */
134static inline char *
135__findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive)
136{
137	int ndx;
138
139	/*
140	 * Find environment variable from end of array (more likely to be
141	 * active).  A variable created by putenv is always active or it is not
142	 * tracked in the array.
143	 */
144	for (ndx = *envNdx; ndx >= 0; ndx--)
145		if (envVars[ndx].putenv) {
146			if (strncmpeq(envVars[ndx].name, name, nameLen)) {
147				*envNdx = ndx;
148				return (envVars[ndx].name + nameLen +
149				    sizeof ("=") - 1);
150			}
151		} else if ((!onlyActive || envVars[ndx].active) &&
152		    (envVars[ndx].nameLen == nameLen &&
153		    strncmpeq(envVars[ndx].name, name, nameLen))) {
154			*envNdx = ndx;
155			return (envVars[ndx].value);
156		}
157
158	return (NULL);
159}
160
161
162/*
163 * Using environ, returns pointer to value associated with name, if any, else
164 * NULL.  Used on the original environ passed into the program.
165 */
166static char *
167__findenv_environ(const char *name, size_t nameLen)
168{
169	int envNdx;
170
171	/* Check for non-existant environment. */
172	if (environ == NULL)
173		return (NULL);
174
175	/* Find variable within environ. */
176	for (envNdx = 0; environ[envNdx] != NULL; envNdx++)
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
283__enlarge_env(void)
284{
285	int newEnvVarsSize;
286	struct envVars *tmpEnvVars;
287
288	envVarsTotal++;
289	if (envVarsTotal > envVarsSize) {
290		newEnvVarsSize = envVarsTotal * 2;
291		tmpEnvVars = realloc(envVars, sizeof (*envVars) *
292		    newEnvVarsSize);
293		if (tmpEnvVars == NULL) {
294			envVarsTotal--;
295			return (false);
296		}
297		envVarsSize = newEnvVarsSize;
298		envVars = tmpEnvVars;
299	}
300
301	return (true);
302}
303
304
305/*
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);
328	if (envVars == NULL)
329		goto Failure;
330
331	/* Copy environ values and keep track of them. */
332	for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) {
333		envVars[envNdx].putenv = false;
334		envVars[envNdx].name =
335		    strdup(environ[envVarsTotal - envNdx - 1]);
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, (int)nameLen,
360			    envVars[envNdx].name);
361			errno = EFAULT;
362			goto Failure;
363		}
364		envVars[activeNdx].active = true;
365	}
366
367	/* Create a new environ. */
368	origEnviron = environ;
369	environ = NULL;
370	if (__rebuild_environ(envVarsTotal) == 0)
371		return (0);
372
373Failure:
374	savedErrno = errno;
375	__clean_env(true);
376	errno = savedErrno;
377
378	return (-1);
379}
380
381
382/*
383 * Destructor function with default argument to __clean_env().
384 */
385static void
386__clean_env_destructor(void)
387{
388	__clean_env(true);
389
390	return;
391}
392
393
394/*
395 * Returns the value of a variable or NULL if none are found.
396 */
397char *
398getenv(const char *name)
399{
400	int envNdx;
401	size_t nameLen;
402
403	/* Check for malformed name. */
404	if (name == NULL || (nameLen = __strleneq(name)) == 0) {
405		errno = EINVAL;
406		return (NULL);
407	}
408
409	/*
410	 * Find environment variable via environ if no changes have been made
411	 * via a *env() call or environ has been replaced by a running program,
412	 * otherwise, use the rebuilt environment.
413	 */
414	if (envVars == NULL || environ != intEnviron)
415		return (__findenv_environ(name, nameLen));
416	else {
417		envNdx = envVarsTotal - 1;
418		return (__findenv(name, nameLen, &envNdx, true));
419	}
420}
421
422
423/*
424 * Set the value of a variable.  Older settings are labeled as inactive.  If an
425 * older setting has enough room to store the new value, it will be reused.  No
426 * previous variables are ever freed here to avoid causing a segmentation fault
427 * in a user's code.
428 *
429 * The variables nameLen and valueLen are passed into here to allow the caller
430 * to calculate the length by means besides just strlen().
431 */
432static int
433__setenv(const char *name, size_t nameLen, const char *value, int overwrite)
434{
435	bool reuse;
436	char *env;
437	int envNdx;
438	int newEnvActive;
439	size_t valueLen;
440
441	/* Find existing environment variable large enough to use. */
442	envNdx = envVarsTotal - 1;
443	newEnvActive = envActive;
444	valueLen = strlen(value);
445	reuse = false;
446	if (__findenv(name, nameLen, &envNdx, false) != NULL) {
447		/* Deactivate entry if overwrite is allowed. */
448		if (envVars[envNdx].active) {
449			if (overwrite == 0)
450				return (0);
451			envVars[envNdx].active = false;
452			newEnvActive--;
453		}
454
455		/* putenv() created variable cannot be reused. */
456		if (envVars[envNdx].putenv)
457			__remove_putenv(envNdx);
458
459		/* Entry is large enough to reuse. */
460		else if (envVars[envNdx].valueSize >= valueLen)
461			reuse = true;
462	}
463
464	/* Create new variable if none was found of sufficient size. */
465	if (! reuse) {
466		/* Enlarge environment. */
467		envNdx = envVarsTotal;
468		if (!__enlarge_env())
469			return (-1);
470
471		/* Create environment entry. */
472		envVars[envNdx].name = malloc(nameLen + sizeof ("=") +
473		    valueLen);
474		if (envVars[envNdx].name == NULL) {
475			envVarsTotal--;
476			return (-1);
477		}
478		envVars[envNdx].nameLen = nameLen;
479		envVars[envNdx].valueSize = valueLen;
480
481		/* Save name of name/value pair. */
482		env = stpcpy(envVars[envNdx].name, name);
483		if ((envVars[envNdx].name)[nameLen] != '=')
484			env = stpcpy(env, "=");
485	}
486	else
487		env = envVars[envNdx].value;
488
489	/* Save value of name/value pair. */
490	strcpy(env, value);
491	envVars[envNdx].value = env;
492	envVars[envNdx].active = true;
493	newEnvActive++;
494
495	/* No need to rebuild environ if an active variable was reused. */
496	if (reuse && newEnvActive == envActive)
497		return (0);
498	else
499		return (__rebuild_environ(newEnvActive));
500}
501
502
503/*
504 * If the program attempts to replace the array of environment variables
505 * (environ) environ, then deactivate all variables and merge in the new list
506 * from environ.
507 */
508static int
509__merge_environ(void)
510{
511	char **env;
512	char *equals;
513
514	/* environ has been replaced.  clean up everything. */
515	if (envVarsTotal > 0 && environ != intEnviron) {
516		/* Deactivate all environment variables. */
517		if (envActive > 0) {
518			origEnviron = NULL;
519			__clean_env(false);
520		}
521
522		/*
523		 * Insert new environ into existing, yet deactivated,
524		 * environment array.
525		 */
526		origEnviron = environ;
527		if (origEnviron != NULL)
528			for (env = origEnviron; *env != NULL; env++) {
529				if ((equals = strchr(*env, '=')) == NULL) {
530					warnx(CorruptEnvValueMsg, *env);
531					errno = EFAULT;
532					return (-1);
533				}
534				if (__setenv(*env, equals - *env, equals + 1,
535				    1) == -1)
536					return (-1);
537			}
538	}
539
540	return (0);
541}
542
543
544/*
545 * The exposed setenv() that peforms a few tests before calling the function
546 * (__setenv()) that does the actual work of inserting a variable into the
547 * environment.
548 */
549int
550setenv(const char *name, const char *value, int overwrite)
551{
552	size_t nameLen;
553
554	/* Check for malformed name. */
555	if (name == NULL || (nameLen = __strleneq(name)) == 0) {
556		errno = EINVAL;
557		return (-1);
558	}
559
560	/* Initialize environment. */
561	if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
562		return (-1);
563
564	return (__setenv(name, nameLen, value, overwrite));
565}
566
567
568/*
569 * Insert a "name=value" string into the environment.  Special settings must be
570 * made to keep setenv() from reusing this memory block and unsetenv() from
571 * allowing it to be tracked.
572 */
573int
574putenv(char *string)
575{
576	char *equals;
577	int envNdx;
578	int newEnvActive;
579	size_t nameLen;
580
581	/* Check for malformed argument. */
582	if (string == NULL || (equals = strchr(string, '=')) == NULL ||
583	    (nameLen = equals - string) == 0) {
584		errno = EINVAL;
585		return (-1);
586	}
587
588	/* Initialize environment. */
589	if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
590		return (-1);
591
592	/* Deactivate previous environment variable. */
593	envNdx = envVarsTotal - 1;
594	newEnvActive = envActive;
595	if (__findenv(string, nameLen, &envNdx, true) != NULL) {
596		/* Reuse previous putenv slot. */
597		if (envVars[envNdx].putenv) {
598			envVars[envNdx].name = string;
599			return (__rebuild_environ(envActive));
600		} else {
601			newEnvActive--;
602			envVars[envNdx].active = false;
603		}
604	}
605
606	/* Enlarge environment. */
607	envNdx = envVarsTotal;
608	if (!__enlarge_env())
609		return (-1);
610
611	/* Create environment entry. */
612	envVars[envNdx].name = string;
613	envVars[envNdx].nameLen = -1;
614	envVars[envNdx].value = NULL;
615	envVars[envNdx].valueSize = -1;
616	envVars[envNdx].putenv = true;
617	envVars[envNdx].active = true;
618	newEnvActive++;
619
620	return (__rebuild_environ(newEnvActive));
621}
622
623
624/*
625 * Unset variable with the same name by flagging it as inactive.  No variable is
626 * ever freed.
627 */
628int
629unsetenv(const char *name)
630{
631	int envNdx;
632	size_t nameLen;
633
634	/* Check for malformed name. */
635	if (name == NULL || (nameLen = __strleneq(name)) == 0) {
636		errno = EINVAL;
637		return (-1);
638	}
639
640	/* Initialize environment. */
641	if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
642		return (-1);
643
644	/* Deactivate specified variable. */
645	envNdx = envVarsTotal - 1;
646	if (__findenv(name, nameLen, &envNdx, true) != NULL) {
647		envVars[envNdx].active = false;
648		if (envVars[envNdx].putenv)
649			__remove_putenv(envNdx);
650		__rebuild_environ(envActive - 1);
651	}
652
653	return (0);
654}
655