getenv.c revision 171195
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 171195 2007-07-04 00:00:41Z scf $");
37
38
39/*
40 * Standard environ.  environ variable is exposed to entire process.
41 *
42 *	origEnviron:	Upon cleanup on unloading of library or failure, this
43 *			allows environ to return to as it was before.
44 *	environSize:	Number of variables environ can hold.  Can only
45 *			increase.
46 */
47extern char **environ;
48static char **origEnviron;
49static int environSize = 0;
50
51/*
52 * Array of environment variables built from environ.  Each element records:
53 *	name:		Pointer to name=value string
54 *	name length:	Length of name not counting '=' character
55 *	value:		Pointer to value within same string as name
56 *	value size:	Size (not length) of space for value not counting the
57 *			nul character
58 *	active state:	true/false value to signify whether variable is active.
59 *			Useful since multiple variables with the same name can
60 *			co-exist.  At most, one variable can be active at any
61 *			one time.
62 *	putenv:		Created from putenv() call.  This memory must not be
63 *			reused.
64 */
65static struct envVars {
66	size_t nameLen;
67	size_t valueSize;
68	char *name;
69	char *value;
70	bool active;
71	bool putenv;
72} *envVars = NULL;
73
74/*
75 * Environment array information.
76 *
77 *	envActive:	Number of active variables in array.
78 *	envVarsSize:	Size of array.
79 *	envVarsTotal:	Number of total variables in array (active or not).
80 */
81static int envActive = 0;
82static int envVarsSize = 0;
83static int envVarsTotal = 0;
84
85
86/* Deinitialization of new environment. */
87static void __attribute__ ((destructor)) __clean_env(void);
88
89
90/*
91 * Inline strlen() for performance.  Also, perform check for an equals sign.
92 * Cheaper here than peforming a strchr() later.
93 */
94static inline size_t
95__strleneq(const char *str)
96{
97	const char *s;
98
99	for (s = str; *s != '\0'; ++s)
100		if (*s == '=')
101			return (0);
102
103	return (s - str);
104}
105
106
107/*
108 * Comparison of an environment name=value to a name.
109 */
110static inline bool
111strncmpeq(const char *nameValue, const char *name, size_t nameLen)
112{
113	if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=')
114		return (true);
115
116	return (false);
117}
118
119
120/*
121 * Using environment, returns pointer to value associated with name, if any,
122 * else NULL.  If the onlyActive flag is set to true, only variables that are
123 * active are returned else all are.
124 */
125static inline char *
126__findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive)
127{
128	int ndx;
129
130	/*
131	 * Find environment variable from end of array (more likely to be
132	 * active).  A variable created by putenv is always active or it is not
133	 * tracked in the array.
134	 */
135	for (ndx = *envNdx; ndx >= 0; ndx--)
136		if (envVars[ndx].putenv) {
137			if (strncmpeq(envVars[ndx].name, name, nameLen)) {
138				*envNdx = ndx;
139				return (envVars[ndx].name + nameLen +
140				    sizeof ("=") - 1);
141			}
142		} else if ((!onlyActive || envVars[ndx].active) &&
143		    (envVars[ndx].nameLen == nameLen &&
144		    strncmpeq(envVars[ndx].name, name, nameLen))) {
145			*envNdx = ndx;
146			return (envVars[ndx].value);
147		}
148
149	return (NULL);
150}
151
152
153/*
154 * Using environ, returns pointer to value associated with name, if any, else
155 * NULL.  Used on the original environ passed into the program.
156 */
157static char *
158__findenv_environ(const char *name, size_t nameLen)
159{
160	int envNdx;
161
162	/* Check for non-existant environment. */
163	if (environ == NULL)
164		return (NULL);
165
166	/* Find variable within environ. */
167	for (envNdx = 0; environ[envNdx] != NULL; envNdx++)
168		if (strncmpeq(environ[envNdx], name, nameLen))
169			return (&(environ[envNdx][nameLen + sizeof("=") - 1]));
170
171	return (NULL);
172}
173
174
175/*
176 * Using the environment, rebuild the environ array for use by other C library
177 * calls that depend upon it.
178 */
179static int
180__rebuild_environ(int newEnvironSize)
181{
182	char **tmpEnviron;
183	int envNdx;
184	int environNdx;
185	int tmpEnvironSize;
186
187	/* Resize environ. */
188	if (newEnvironSize > environSize) {
189		tmpEnvironSize = newEnvironSize * 2;
190		tmpEnviron = realloc(environ, sizeof (*environ) *
191		    (tmpEnvironSize + 1));
192		if (tmpEnviron == NULL)
193			return (-1);
194		environSize = tmpEnvironSize;
195		environ = tmpEnviron;
196	}
197	envActive = newEnvironSize;
198
199	/* Assign active variables to environ. */
200	for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--)
201		if (envVars[envNdx].active)
202			environ[environNdx++] = envVars[envNdx].name;
203	environ[environNdx] = NULL;
204
205	return (0);
206}
207
208
209/*
210 * Enlarge new environment.
211 */
212static inline bool
213__enlarge_env(void)
214{
215	int newEnvVarsSize;
216	struct envVars *tmpEnvVars;
217
218	envVarsTotal++;
219	if (envVarsTotal > envVarsSize) {
220		newEnvVarsSize = envVarsTotal * 2;
221		tmpEnvVars = realloc(envVars, sizeof (*envVars) *
222		    newEnvVarsSize);
223		if (tmpEnvVars == NULL) {
224			envVarsTotal--;
225			return (false);
226		}
227		envVarsSize = newEnvVarsSize;
228		envVars = tmpEnvVars;
229	}
230
231	return (true);
232}
233
234
235/*
236 * Using environ, build an environment for use by standard C library calls.
237 */
238static int
239__build_env(void)
240{
241	char **env;
242	int activeNdx;
243	int envNdx;
244	int rtrnVal;
245	int savedErrno;
246	size_t nameLen;
247
248	/* Check for non-existant environment. */
249	if (environ == NULL)
250		return (0);
251	if (environ[0] == NULL)
252		goto SaveEnviron;
253
254	/* Count environment variables. */
255	for (env = environ, envVarsTotal = 0; *env != NULL; env++)
256		envVarsTotal++;
257	envVarsSize = envVarsTotal * 2;
258
259	/* Create new environment. */
260	envVars = calloc(1, sizeof (*envVars) * envVarsSize);
261	if (envVars == NULL)
262		goto Failure;
263
264	/* Copy environ values and keep track of them. */
265	for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) {
266		envVars[envNdx].putenv = false;
267		envVars[envNdx].name =
268		    strdup(environ[envVarsTotal - envNdx - 1]);
269		if (envVars[envNdx].name == NULL)
270			goto Failure;
271		envVars[envNdx].value = strchr(envVars[envNdx].name, '=');
272		if (envVars[envNdx].value != NULL) {
273			envVars[envNdx].value++;
274			envVars[envNdx].valueSize =
275			    strlen(envVars[envNdx].value);
276		} else {
277			warnx("environment corrupt; missing value for %s",
278			    envVars[envNdx].name);
279			errno = EFAULT;
280			goto Failure;
281		}
282
283		/*
284		 * Find most current version of variable to make active.  This
285		 * will prevent multiple active variables from being created
286		 * during this initialization phase.
287		 */
288		nameLen = envVars[envNdx].value - envVars[envNdx].name - 1;
289		envVars[envNdx].nameLen = nameLen;
290		activeNdx = envVarsTotal - 1;
291		if (__findenv(envVars[envNdx].name, nameLen, &activeNdx,
292		    false) == NULL) {
293			warnx("environment corrupt; unable to find %.*s",
294			    nameLen, envVars[envNdx].name);
295			errno = EFAULT;
296			goto Failure;
297		}
298		envVars[activeNdx].active = true;
299	}
300
301	/* Create a new environ. */
302SaveEnviron:
303	origEnviron = environ;
304	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;
314
315	return (rtrnVal);
316
317Failure:
318	savedErrno = errno;
319	__clean_env();
320	errno = savedErrno;
321
322	return (-1);
323}
324
325
326/*
327 * Remove variable added by putenv() from variable tracking array.
328 */
329static void
330__remove_putenv(int envNdx)
331{
332	memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]),
333	    (envVarsTotal - envNdx) * sizeof (*envVars));
334	envVarsTotal--;
335
336	return;
337}
338
339
340/*
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/*
369 * Returns the value of a variable or NULL if none are found.
370 */
371char *
372getenv(const char *name)
373{
374	int envNdx;
375	size_t nameLen;
376
377	/* Check for malformed name. */
378	if (name == NULL || (nameLen = __strleneq(name)) == 0) {
379		errno = EINVAL;
380		return (NULL);
381	}
382
383	/* Find environment variable via environ or rebuilt environment. */
384	if (envVars == NULL)
385		return (__findenv_environ(name, nameLen));
386	else {
387		envNdx = envVarsTotal - 1;
388		return (__findenv(name, nameLen, &envNdx, true));
389	}
390}
391
392
393/*
394 * Set the value of a variable.  Older settings are labeled as inactive.  If an
395 * older setting has enough room to store the new value, it will be reused.  No
396 * previous variables are ever freed here to avoid causing a segmentation fault
397 * in a user's code.
398 */
399int
400setenv(const char *name, const char *value, int overwrite)
401{
402	bool reuse;
403	char *env;
404	int envNdx;
405	int newEnvActive;
406	size_t nameLen;
407	size_t valueLen;
408
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
419	/* Find existing environment variable large enough to use. */
420	envNdx = envVarsTotal - 1;
421	newEnvActive = envActive;
422	valueLen = strlen(value);
423	reuse = false;
424	if (__findenv(name, nameLen, &envNdx, false) != NULL) {
425		/* Deactivate entry if overwrite is allowed. */
426		if (envVars[envNdx].active) {
427			if (overwrite == 0)
428				return (0);
429			envVars[envNdx].active = false;
430			newEnvActive--;
431		}
432
433		/* putenv() created variable cannot be reused. */
434		if (envVars[envNdx].putenv)
435			__remove_putenv(envNdx);
436
437		/* Entry is large enough to reuse. */
438		else if (envVars[envNdx].valueSize >= valueLen)
439			reuse = true;
440
441	}
442
443	/* Create new variable if none was found of sufficient size. */
444	if (! reuse) {
445		/* Enlarge environment. */
446		envNdx = envVarsTotal;
447		if (!__enlarge_env())
448			return (-1);
449
450		/* Create environment entry. */
451		envVars[envNdx].name = malloc(nameLen + sizeof ("=") +
452		    valueLen);
453		if (envVars[envNdx].name == NULL) {
454			envVarsTotal--;
455			return (-1);
456		}
457		envVars[envNdx].nameLen = nameLen;
458		envVars[envNdx].valueSize = valueLen;
459
460		/* Save name of name/value pair. */
461		env = stpcpy(envVars[envNdx].name, name);
462		if ((envVars[envNdx].name)[nameLen] != '=')
463			env = stpcpy(env, "=");
464	}
465	else
466		env = envVars[envNdx].value;
467
468	/* Save value of name/value pair. */
469	strcpy(env, value);
470	envVars[envNdx].value = env;
471	envVars[envNdx].active = true;
472	newEnvActive++;
473
474	/* No need to rebuild environ if the variable was reused. */
475	if (reuse)
476		return (0);
477	else
478		return (__rebuild_environ(newEnvActive));
479}
480
481
482/*
483 * Insert a "name=value" string into then environment.  Special settings must be
484 * made to keep setenv() from reusing this memory block and unsetenv() from
485 * allowing it to be tracked.
486 */
487int
488putenv(char *string)
489{
490	char *equals;
491	int envNdx;
492	int newEnvActive;
493	size_t nameLen;
494
495	/* Check for malformed argument. */
496	if (string == NULL || (equals = strchr(string, '=')) == NULL ||
497	    (nameLen = equals - string) == 0) {
498		errno = EINVAL;
499		return (-1);
500	}
501
502	/* Initialize environment. */
503	if (envVars == NULL && __build_env() == -1)
504		return (-1);
505
506	/* Deactivate previous environment variable. */
507	envNdx = envVarsTotal - 1;
508	newEnvActive = envActive;
509	if (__findenv(string, nameLen, &envNdx, true) != NULL) {
510		/* Reuse previous putenv slot. */
511		if (envVars[envNdx].putenv) {
512			envVars[envNdx].name = string;
513			return (__rebuild_environ(envActive));
514		} else {
515			newEnvActive--;
516			envVars[envNdx].active = false;
517		}
518	}
519
520	/* Enlarge environment. */
521	envNdx = envVarsTotal;
522	if (!__enlarge_env())
523		return (-1);
524
525	/* Create environment entry. */
526	envVars[envNdx].name = string;
527	envVars[envNdx].nameLen = -1;
528	envVars[envNdx].value = NULL;
529	envVars[envNdx].valueSize = -1;
530	envVars[envNdx].putenv = true;
531	envVars[envNdx].active = true;
532	newEnvActive++;
533
534	return (__rebuild_environ(newEnvActive));
535}
536
537
538/*
539 * Unset variable with the same name by flagging it as inactive.  No variable is
540 * ever freed.
541 */
542int
543unsetenv(const char *name)
544{
545	int envNdx;
546	size_t nameLen;
547
548	/* Check for malformed name. */
549	if (name == NULL || (nameLen = __strleneq(name)) == 0) {
550		errno = EINVAL;
551		return (-1);
552	}
553
554	/* Initialize environment. */
555	if (envVars == NULL && __build_env() == -1)
556		return (-1);
557
558	/* Deactivate specified variable. */
559	envNdx = envVarsTotal - 1;
560	if (__findenv(name, nameLen, &envNdx, true) != NULL) {
561		envVars[envNdx].active = false;
562		if (envVars[envNdx].putenv)
563			__remove_putenv(envNdx);
564		__rebuild_environ(envActive - 1);
565	}
566
567	return (0);
568}
569