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