xref: /freebsd/lib/libc/stdlib/getenv.c (revision 3abc9103eb34adbb48853f2361206eba207d6c4f)
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$");
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  */
47 extern char **environ;
48 static char **origEnviron;
49 static 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  */
65 static 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  */
81 static int envActive = 0;
82 static int envVarsSize = 0;
83 static int envVarsTotal = 0;
84 
85 
86 /* Deinitialization of new environment. */
87 static 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  */
94 static 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  */
110 static inline bool
111 strncmpeq(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  */
125 static 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  */
157 static 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  */
179 static 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  */
212 static 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  */
238 static 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. */
302 SaveEnviron:
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 
317 Failure:
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  */
329 static 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  */
344 static 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  */
371 char *
372 getenv(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  */
399 int
400 setenv(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  */
487 int
488 putenv(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  */
542 int
543 unsetenv(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