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