]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/stdlib/getenv.c
exit(3): Add HISTORY section
[FreeBSD/FreeBSD.git] / lib / libc / stdlib / getenv.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2007-2009 Sean C. Farley <scf@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer,
12  *    without modification, immediately at the beginning of the file.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32
33 #include "namespace.h"
34 #include <sys/types.h>
35 #include <errno.h>
36 #include <stdbool.h>
37 #include <stddef.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include "un-namespace.h"
42
43
44 static const char CorruptEnvFindMsg[] = "environment corrupt; unable to find ";
45 static const char CorruptEnvValueMsg[] =
46     "environment corrupt; missing value for ";
47
48
49 /*
50  * Standard environ.  environ variable is exposed to entire process.
51  *
52  *      origEnviron:    Upon cleanup on unloading of library or failure, this
53  *                      allows environ to return to as it was before.
54  *      environSize:    Number of variables environ can hold.  Can only
55  *                      increase.
56  *      intEnviron:     Internally-built environ.  Exposed via environ during
57  *                      (re)builds of the environment.
58  */
59 extern char **environ;
60 static char **origEnviron;
61 static char **intEnviron = NULL;
62 static int environSize = 0;
63
64 /*
65  * Array of environment variables built from environ.  Each element records:
66  *      name:           Pointer to name=value string
67  *      name length:    Length of name not counting '=' character
68  *      value:          Pointer to value within same string as name
69  *      value size:     Size (not length) of space for value not counting the
70  *                      nul character
71  *      active state:   true/false value to signify whether variable is active.
72  *                      Useful since multiple variables with the same name can
73  *                      co-exist.  At most, one variable can be active at any
74  *                      one time.
75  *      putenv:         Created from putenv() call.  This memory must not be
76  *                      reused.
77  */
78 static struct envVars {
79         size_t nameLen;
80         size_t valueSize;
81         char *name;
82         char *value;
83         bool active;
84         bool putenv;
85 } *envVars = NULL;
86
87 /*
88  * Environment array information.
89  *
90  *      envActive:      Number of active variables in array.
91  *      envVarsSize:    Size of array.
92  *      envVarsTotal:   Number of total variables in array (active or not).
93  */
94 static int envActive = 0;
95 static int envVarsSize = 0;
96 static int envVarsTotal = 0;
97
98
99 /* Deinitialization of new environment. */
100 static void __attribute__ ((destructor)) __clean_env_destructor(void);
101
102
103 /*
104  * A simple version of warnx() to avoid the bloat of including stdio in static
105  * binaries.
106  */
107 static void
108 __env_warnx(const char *msg, const char *name, size_t nameLen)
109 {
110         static const char nl[] = "\n";
111         static const char progSep[] = ": ";
112
113         _write(STDERR_FILENO, _getprogname(), strlen(_getprogname()));
114         _write(STDERR_FILENO, progSep, sizeof(progSep) - 1);
115         _write(STDERR_FILENO, msg, strlen(msg));
116         _write(STDERR_FILENO, name, nameLen);
117         _write(STDERR_FILENO, nl, sizeof(nl) - 1);
118
119         return;
120 }
121
122
123 /*
124  * Inline strlen() for performance.  Also, perform check for an equals sign.
125  * Cheaper here than peforming a strchr() later.
126  */
127 static inline size_t
128 __strleneq(const char *str)
129 {
130         const char *s;
131
132         for (s = str; *s != '\0'; ++s)
133                 if (*s == '=')
134                         return (0);
135
136         return (s - str);
137 }
138
139
140 /*
141  * Comparison of an environment name=value to a name.
142  */
143 static inline bool
144 strncmpeq(const char *nameValue, const char *name, size_t nameLen)
145 {
146         if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=')
147                 return (true);
148
149         return (false);
150 }
151
152
153 /*
154  * Using environment, returns pointer to value associated with name, if any,
155  * else NULL.  If the onlyActive flag is set to true, only variables that are
156  * active are returned else all are.
157  */
158 static inline char *
159 __findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive)
160 {
161         int ndx;
162
163         /*
164          * Find environment variable from end of array (more likely to be
165          * active).  A variable created by putenv is always active, or it is not
166          * tracked in the array.
167          */
168         for (ndx = *envNdx; ndx >= 0; ndx--)
169                 if (envVars[ndx].putenv) {
170                         if (strncmpeq(envVars[ndx].name, name, nameLen)) {
171                                 *envNdx = ndx;
172                                 return (envVars[ndx].name + nameLen +
173                                     sizeof ("=") - 1);
174                         }
175                 } else if ((!onlyActive || envVars[ndx].active) &&
176                     (envVars[ndx].nameLen == nameLen &&
177                     strncmpeq(envVars[ndx].name, name, nameLen))) {
178                         *envNdx = ndx;
179                         return (envVars[ndx].value);
180                 }
181
182         return (NULL);
183 }
184
185
186 /*
187  * Using environ, returns pointer to value associated with name, if any, else
188  * NULL.  Used on the original environ passed into the program.
189  */
190 static char *
191 __findenv_environ(const char *name, size_t nameLen)
192 {
193         int envNdx;
194
195         /* Find variable within environ. */
196         for (envNdx = 0; environ[envNdx] != NULL; envNdx++)
197                 if (strncmpeq(environ[envNdx], name, nameLen))
198                         return (&(environ[envNdx][nameLen + sizeof("=") - 1]));
199
200         return (NULL);
201 }
202
203
204 /*
205  * Remove variable added by putenv() from variable tracking array.
206  */
207 static void
208 __remove_putenv(int envNdx)
209 {
210         envVarsTotal--;
211         if (envVarsTotal > envNdx)
212                 memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]),
213                     (envVarsTotal - envNdx) * sizeof (*envVars));
214         memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars));
215
216         return;
217 }
218
219
220 /*
221  * Deallocate the environment built from environ as well as environ then set
222  * both to NULL.  Eases debugging of memory leaks.
223  */
224 static void
225 __clean_env(bool freeVars)
226 {
227         int envNdx;
228
229         /* Deallocate environment and environ if created by *env(). */
230         if (envVars != NULL) {
231                 for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--)
232                         /* Free variables or deactivate them. */
233                         if (envVars[envNdx].putenv) {
234                                 if (!freeVars)
235                                         __remove_putenv(envNdx);
236                         } else {
237                                 if (freeVars)
238                                         free(envVars[envNdx].name);
239                                 else
240                                         envVars[envNdx].active = false;
241                         }
242                 if (freeVars) {
243                         free(envVars);
244                         envVars = NULL;
245                 } else
246                         envActive = 0;
247
248                 /* Restore original environ if it has not updated by program. */
249                 if (origEnviron != NULL) {
250                         if (environ == intEnviron)
251                                 environ = origEnviron;
252                         free(intEnviron);
253                         intEnviron = NULL;
254                         environSize = 0;
255                 }
256         }
257
258         return;
259 }
260
261
262 /*
263  * Using the environment, rebuild the environ array for use by other C library
264  * calls that depend upon it.
265  */
266 static int
267 __rebuild_environ(int newEnvironSize)
268 {
269         char **tmpEnviron;
270         int envNdx;
271         int environNdx;
272         int tmpEnvironSize;
273
274         /* Resize environ. */
275         if (newEnvironSize > environSize) {
276                 tmpEnvironSize = newEnvironSize * 2;
277                 tmpEnviron = reallocarray(intEnviron, tmpEnvironSize + 1,
278                     sizeof(*intEnviron));
279                 if (tmpEnviron == NULL)
280                         return (-1);
281                 environSize = tmpEnvironSize;
282                 intEnviron = tmpEnviron;
283         }
284         envActive = newEnvironSize;
285
286         /* Assign active variables to environ. */
287         for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--)
288                 if (envVars[envNdx].active)
289                         intEnviron[environNdx++] = envVars[envNdx].name;
290         intEnviron[environNdx] = NULL;
291
292         /* Always set environ which may have been replaced by program. */
293         environ = intEnviron;
294
295         return (0);
296 }
297
298
299 /*
300  * Enlarge new environment.
301  */
302 static inline bool
303 __enlarge_env(void)
304 {
305         int newEnvVarsSize;
306         struct envVars *tmpEnvVars;
307
308         envVarsTotal++;
309         if (envVarsTotal > envVarsSize) {
310                 newEnvVarsSize = envVarsTotal * 2;
311                 tmpEnvVars = reallocarray(envVars, newEnvVarsSize,
312                     sizeof(*envVars));
313                 if (tmpEnvVars == NULL) {
314                         envVarsTotal--;
315                         return (false);
316                 }
317                 envVarsSize = newEnvVarsSize;
318                 envVars = tmpEnvVars;
319         }
320
321         return (true);
322 }
323
324
325 /*
326  * Using environ, build an environment for use by standard C library calls.
327  */
328 static int
329 __build_env(void)
330 {
331         char **env;
332         int activeNdx;
333         int envNdx;
334         int savedErrno;
335         size_t nameLen;
336
337         /* Check for non-existant environment. */
338         if (environ == NULL || environ[0] == NULL)
339                 return (0);
340
341         /* Count environment variables. */
342         for (env = environ, envVarsTotal = 0; *env != NULL; env++)
343                 envVarsTotal++;
344         envVarsSize = envVarsTotal * 2;
345
346         /* Create new environment. */
347         envVars = calloc(envVarsSize, sizeof(*envVars));
348         if (envVars == NULL)
349                 goto Failure;
350
351         /* Copy environ values and keep track of them. */
352         for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) {
353                 envVars[envNdx].putenv = false;
354                 envVars[envNdx].name =
355                     strdup(environ[envVarsTotal - envNdx - 1]);
356                 if (envVars[envNdx].name == NULL)
357                         goto Failure;
358                 envVars[envNdx].value = strchr(envVars[envNdx].name, '=');
359                 if (envVars[envNdx].value != NULL) {
360                         envVars[envNdx].value++;
361                         envVars[envNdx].valueSize =
362                             strlen(envVars[envNdx].value);
363                 } else {
364                         __env_warnx(CorruptEnvValueMsg, envVars[envNdx].name,
365                             strlen(envVars[envNdx].name));
366                         errno = EFAULT;
367                         goto Failure;
368                 }
369
370                 /*
371                  * Find most current version of variable to make active.  This
372                  * will prevent multiple active variables from being created
373                  * during this initialization phase.
374                  */
375                 nameLen = envVars[envNdx].value - envVars[envNdx].name - 1;
376                 envVars[envNdx].nameLen = nameLen;
377                 activeNdx = envVarsTotal - 1;
378                 if (__findenv(envVars[envNdx].name, nameLen, &activeNdx,
379                     false) == NULL) {
380                         __env_warnx(CorruptEnvFindMsg, envVars[envNdx].name,
381                             nameLen);
382                         errno = EFAULT;
383                         goto Failure;
384                 }
385                 envVars[activeNdx].active = true;
386         }
387
388         /* Create a new environ. */
389         origEnviron = environ;
390         environ = NULL;
391         if (__rebuild_environ(envVarsTotal) == 0)
392                 return (0);
393
394 Failure:
395         savedErrno = errno;
396         __clean_env(true);
397         errno = savedErrno;
398
399         return (-1);
400 }
401
402
403 /*
404  * Destructor function with default argument to __clean_env().
405  */
406 static void
407 __clean_env_destructor(void)
408 {
409         __clean_env(true);
410
411         return;
412 }
413
414
415 /*
416  * Returns the value of a variable or NULL if none are found.
417  */
418 char *
419 getenv(const char *name)
420 {
421         int envNdx;
422         size_t nameLen;
423
424         /* Check for malformed name. */
425         if (name == NULL || (nameLen = __strleneq(name)) == 0) {
426                 errno = EINVAL;
427                 return (NULL);
428         }
429
430         /*
431          * Variable search order:
432          * 1. Check for an empty environ.  This allows an application to clear
433          *    the environment.
434          * 2. Search the external environ array.
435          * 3. Search the internal environment.
436          *
437          * Since malloc() depends upon getenv(), getenv() must never cause the
438          * internal environment storage to be generated.
439          */
440         if (environ == NULL || environ[0] == NULL)
441                 return (NULL);
442         else if (envVars == NULL || environ != intEnviron)
443                 return (__findenv_environ(name, nameLen));
444         else {
445                 envNdx = envVarsTotal - 1;
446                 return (__findenv(name, nameLen, &envNdx, true));
447         }
448 }
449
450
451 /*
452  * Set the value of a variable.  Older settings are labeled as inactive.  If an
453  * older setting has enough room to store the new value, it will be reused.  No
454  * previous variables are ever freed here to avoid causing a segmentation fault
455  * in a user's code.
456  *
457  * The variables nameLen and valueLen are passed into here to allow the caller
458  * to calculate the length by means besides just strlen().
459  */
460 static int
461 __setenv(const char *name, size_t nameLen, const char *value, int overwrite)
462 {
463         bool reuse;
464         char *env;
465         int envNdx;
466         int newEnvActive;
467         size_t valueLen;
468
469         /* Find existing environment variable large enough to use. */
470         envNdx = envVarsTotal - 1;
471         newEnvActive = envActive;
472         valueLen = strlen(value);
473         reuse = false;
474         if (__findenv(name, nameLen, &envNdx, false) != NULL) {
475                 /* Deactivate entry if overwrite is allowed. */
476                 if (envVars[envNdx].active) {
477                         if (overwrite == 0)
478                                 return (0);
479                         envVars[envNdx].active = false;
480                         newEnvActive--;
481                 }
482
483                 /* putenv() created variable cannot be reused. */
484                 if (envVars[envNdx].putenv)
485                         __remove_putenv(envNdx);
486
487                 /* Entry is large enough to reuse. */
488                 else if (envVars[envNdx].valueSize >= valueLen)
489                         reuse = true;
490         }
491
492         /* Create new variable if none was found of sufficient size. */
493         if (! reuse) {
494                 /* Enlarge environment. */
495                 envNdx = envVarsTotal;
496                 if (!__enlarge_env())
497                         return (-1);
498
499                 /* Create environment entry. */
500                 envVars[envNdx].name = malloc(nameLen + sizeof ("=") +
501                     valueLen);
502                 if (envVars[envNdx].name == NULL) {
503                         envVarsTotal--;
504                         return (-1);
505                 }
506                 envVars[envNdx].nameLen = nameLen;
507                 envVars[envNdx].valueSize = valueLen;
508
509                 /* Save name of name/value pair. */
510                 env = stpncpy(envVars[envNdx].name, name, nameLen);
511                 *env++ = '=';
512         }
513         else
514                 env = envVars[envNdx].value;
515
516         /* Save value of name/value pair. */
517         strcpy(env, value);
518         envVars[envNdx].value = env;
519         envVars[envNdx].active = true;
520         newEnvActive++;
521
522         /* No need to rebuild environ if an active variable was reused. */
523         if (reuse && newEnvActive == envActive)
524                 return (0);
525         else
526                 return (__rebuild_environ(newEnvActive));
527 }
528
529
530 /*
531  * If the program attempts to replace the array of environment variables
532  * (environ) environ or sets the first varible to NULL, then deactivate all
533  * variables and merge in the new list from environ.
534  */
535 static int
536 __merge_environ(void)
537 {
538         char **env;
539         char *equals;
540
541         /*
542          * Internally-built environ has been replaced or cleared (detected by
543          * using the count of active variables against a NULL as the first value
544          * in environ).  Clean up everything.
545          */
546         if (intEnviron != NULL && (environ != intEnviron || (envActive > 0 &&
547             environ[0] == NULL))) {
548                 /* Deactivate all environment variables. */
549                 if (envActive > 0) {
550                         origEnviron = NULL;
551                         __clean_env(false);
552                 }
553
554                 /*
555                  * Insert new environ into existing, yet deactivated,
556                  * environment array.
557                  */
558                 origEnviron = environ;
559                 if (origEnviron != NULL)
560                         for (env = origEnviron; *env != NULL; env++) {
561                                 if ((equals = strchr(*env, '=')) == NULL) {
562                                         __env_warnx(CorruptEnvValueMsg, *env,
563                                             strlen(*env));
564                                         errno = EFAULT;
565                                         return (-1);
566                                 }
567                                 if (__setenv(*env, equals - *env, equals + 1,
568                                     1) == -1)
569                                         return (-1);
570                         }
571         }
572
573         return (0);
574 }
575
576
577 /*
578  * The exposed setenv() that peforms a few tests before calling the function
579  * (__setenv()) that does the actual work of inserting a variable into the
580  * environment.
581  */
582 int
583 setenv(const char *name, const char *value, int overwrite)
584 {
585         size_t nameLen;
586
587         /* Check for malformed name. */
588         if (name == NULL || (nameLen = __strleneq(name)) == 0) {
589                 errno = EINVAL;
590                 return (-1);
591         }
592
593         /* Initialize environment. */
594         if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
595                 return (-1);
596
597         return (__setenv(name, nameLen, value, overwrite));
598 }
599
600
601 /*
602  * Insert a "name=value" string into the environment.  Special settings must be
603  * made to keep setenv() from reusing this memory block and unsetenv() from
604  * allowing it to be tracked.
605  */
606 int
607 putenv(char *string)
608 {
609         char *equals;
610         int envNdx;
611         int newEnvActive;
612         size_t nameLen;
613
614         /* Check for malformed argument. */
615         if (string == NULL || (equals = strchr(string, '=')) == NULL ||
616             (nameLen = equals - string) == 0) {
617                 errno = EINVAL;
618                 return (-1);
619         }
620
621         /* Initialize environment. */
622         if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
623                 return (-1);
624
625         /* Deactivate previous environment variable. */
626         envNdx = envVarsTotal - 1;
627         newEnvActive = envActive;
628         if (__findenv(string, nameLen, &envNdx, true) != NULL) {
629                 /* Reuse previous putenv slot. */
630                 if (envVars[envNdx].putenv) {
631                         envVars[envNdx].name = string;
632                         return (__rebuild_environ(envActive));
633                 } else {
634                         newEnvActive--;
635                         envVars[envNdx].active = false;
636                 }
637         }
638
639         /* Enlarge environment. */
640         envNdx = envVarsTotal;
641         if (!__enlarge_env())
642                 return (-1);
643
644         /* Create environment entry. */
645         envVars[envNdx].name = string;
646         envVars[envNdx].nameLen = -1;
647         envVars[envNdx].value = NULL;
648         envVars[envNdx].valueSize = -1;
649         envVars[envNdx].putenv = true;
650         envVars[envNdx].active = true;
651         newEnvActive++;
652
653         return (__rebuild_environ(newEnvActive));
654 }
655
656
657 /*
658  * Unset variable with the same name by flagging it as inactive.  No variable is
659  * ever freed.
660  */
661 int
662 unsetenv(const char *name)
663 {
664         int envNdx;
665         size_t nameLen;
666         int newEnvActive;
667
668         /* Check for malformed name. */
669         if (name == NULL || (nameLen = __strleneq(name)) == 0) {
670                 errno = EINVAL;
671                 return (-1);
672         }
673
674         /* Initialize environment. */
675         if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
676                 return (-1);
677
678         /* Deactivate specified variable. */
679         /* Remove all occurrences. */
680         envNdx = envVarsTotal - 1;
681         newEnvActive = envActive;
682         while (__findenv(name, nameLen, &envNdx, true) != NULL) {
683                 envVars[envNdx].active = false;
684                 if (envVars[envNdx].putenv)
685                         __remove_putenv(envNdx);
686                 envNdx--;
687                 newEnvActive--;
688         }
689         if (newEnvActive != envActive)
690                 __rebuild_environ(newEnvActive);
691
692         return (0);
693 }