]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - lib/libc/stdlib/getenv.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / lib / libc / stdlib / getenv.c
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
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
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
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 *
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 *
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
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
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
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
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
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
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 *
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
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
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
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
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
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 }