xref: /dragonfly/lib/libc/stdlib/getenv.c (revision 7307c47676255189131ef8c0756ab8f61836c585)
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