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