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