Home | History | Annotate | Line # | Download | only in lib
      1 /* Copyright (C) 1992,1995-1999,2000-2003 Free Software Foundation, Inc.
      2    This file is part of the GNU C Library.
      3 
      4    This program is free software; you can redistribute it and/or modify
      5    it under the terms of the GNU General Public License as published by
      6    the Free Software Foundation; either version 2, or (at your option)
      7    any later version.
      8 
      9    This program is distributed in the hope that it will be useful,
     10    but WITHOUT ANY WARRANTY; without even the implied warranty of
     11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12    GNU General Public License for more details.
     13 
     14    You should have received a copy of the GNU General Public License along
     15    with this program; if not, write to the Free Software Foundation,
     16    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
     17 #include <sys/cdefs.h>
     18 __RCSID("$NetBSD: setenv.c,v 1.2 2016/05/17 14:00:09 christos Exp $");
     19 
     20 
     21 #if HAVE_CONFIG_H
     22 # include <config.h>
     23 #endif
     24 #include <alloca.h>
     25 
     26 #include <errno.h>
     27 #ifndef __set_errno
     28 # define __set_errno(ev) ((errno) = (ev))
     29 #endif
     30 
     31 #include <stdlib.h>
     32 #include <string.h>
     33 #if _LIBC || HAVE_UNISTD_H
     34 # include <unistd.h>
     35 #endif
     36 
     37 #if !_LIBC
     38 # include "allocsa.h"
     39 #endif
     40 
     41 #if !_LIBC
     42 # define __environ	environ
     43 # ifndef HAVE_ENVIRON_DECL
     44 extern char **environ;
     45 # endif
     46 #endif
     47 
     48 #if _LIBC
     49 /* This lock protects against simultaneous modifications of `environ'.  */
     50 # include <bits/libc-lock.h>
     51 __libc_lock_define_initialized (static, envlock)
     52 # define LOCK	__libc_lock_lock (envlock)
     53 # define UNLOCK	__libc_lock_unlock (envlock)
     54 #else
     55 # define LOCK
     56 # define UNLOCK
     57 #endif
     58 
     59 /* In the GNU C library we must keep the namespace clean.  */
     60 #ifdef _LIBC
     61 # define setenv __setenv
     62 # define clearenv __clearenv
     63 # define tfind __tfind
     64 # define tsearch __tsearch
     65 #endif
     66 
     67 /* In the GNU C library implementation we try to be more clever and
     68    allow arbitrarily many changes of the environment given that the used
     69    values are from a small set.  Outside glibc this will eat up all
     70    memory after a while.  */
     71 #if defined _LIBC || (defined HAVE_SEARCH_H && defined HAVE_TSEARCH \
     72 		      && defined __GNUC__)
     73 # define USE_TSEARCH	1
     74 # include <search.h>
     75 typedef int (*compar_fn_t) (const void *, const void *);
     76 
     77 /* This is a pointer to the root of the search tree with the known
     78    values.  */
     79 static void *known_values;
     80 
     81 # define KNOWN_VALUE(Str) \
     82   ({									      \
     83     void *value = tfind (Str, &known_values, (compar_fn_t) strcmp);	      \
     84     value != NULL ? *(char **) value : NULL;				      \
     85   })
     86 # define STORE_VALUE(Str) \
     87   tsearch (Str, &known_values, (compar_fn_t) strcmp)
     88 
     89 #else
     90 # undef USE_TSEARCH
     91 
     92 # define KNOWN_VALUE(Str) NULL
     93 # define STORE_VALUE(Str) do { } while (0)
     94 
     95 #endif
     96 
     97 
     98 /* If this variable is not a null pointer we allocated the current
     99    environment.  */
    100 static char **last_environ;
    101 
    102 
    103 /* This function is used by `setenv' and `putenv'.  The difference between
    104    the two functions is that for the former must create a new string which
    105    is then placed in the environment, while the argument of `putenv'
    106    must be used directly.  This is all complicated by the fact that we try
    107    to reuse values once generated for a `setenv' call since we can never
    108    free the strings.  */
    109 int
    110 __add_to_environ (const char *name, const char *value, const char *combined,
    111 		  int replace)
    112 {
    113   register char **ep;
    114   register size_t size;
    115   const size_t namelen = strlen (name);
    116   const size_t vallen = value != NULL ? strlen (value) + 1 : 0;
    117 
    118   LOCK;
    119 
    120   /* We have to get the pointer now that we have the lock and not earlier
    121      since another thread might have created a new environment.  */
    122   ep = __environ;
    123 
    124   size = 0;
    125   if (ep != NULL)
    126     {
    127       for (; *ep != NULL; ++ep)
    128 	if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
    129 	  break;
    130 	else
    131 	  ++size;
    132     }
    133 
    134   if (ep == NULL || *ep == NULL)
    135     {
    136       char **new_environ;
    137 #ifdef USE_TSEARCH
    138       char *new_value;
    139 #endif
    140 
    141       /* We allocated this space; we can extend it.  */
    142       new_environ =
    143 	(char **) (last_environ == NULL
    144 		   ? malloc ((size + 2) * sizeof (char *))
    145 		   : realloc (last_environ, (size + 2) * sizeof (char *)));
    146       if (new_environ == NULL)
    147 	{
    148 	  UNLOCK;
    149 	  return -1;
    150 	}
    151 
    152       /* If the whole entry is given add it.  */
    153       if (combined != NULL)
    154 	/* We must not add the string to the search tree since it belongs
    155 	   to the user.  */
    156 	new_environ[size] = (char *) combined;
    157       else
    158 	{
    159 	  /* See whether the value is already known.  */
    160 #ifdef USE_TSEARCH
    161 # ifdef _LIBC
    162 	  new_value = (char *) alloca (namelen + 1 + vallen);
    163 	  __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
    164 		     value, vallen);
    165 # else
    166 	  new_value = (char *) allocsa (namelen + 1 + vallen);
    167 	  if (new_value == NULL)
    168 	    {
    169 	      __set_errno (ENOMEM);
    170 	      UNLOCK;
    171 	      return -1;
    172 	    }
    173 	  memcpy (new_value, name, namelen);
    174 	  new_value[namelen] = '=';
    175 	  memcpy (&new_value[namelen + 1], value, vallen);
    176 # endif
    177 
    178 	  new_environ[size] = KNOWN_VALUE (new_value);
    179 	  if (new_environ[size] == NULL)
    180 #endif
    181 	    {
    182 	      new_environ[size] = (char *) malloc (namelen + 1 + vallen);
    183 	      if (new_environ[size] == NULL)
    184 		{
    185 #if defined USE_TSEARCH && !defined _LIBC
    186 		  freesa (new_value);
    187 #endif
    188 		  __set_errno (ENOMEM);
    189 		  UNLOCK;
    190 		  return -1;
    191 		}
    192 
    193 #ifdef USE_TSEARCH
    194 	      memcpy (new_environ[size], new_value, namelen + 1 + vallen);
    195 #else
    196 	      memcpy (new_environ[size], name, namelen);
    197 	      new_environ[size][namelen] = '=';
    198 	      memcpy (&new_environ[size][namelen + 1], value, vallen);
    199 #endif
    200 	      /* And save the value now.  We cannot do this when we remove
    201 		 the string since then we cannot decide whether it is a
    202 		 user string or not.  */
    203 	      STORE_VALUE (new_environ[size]);
    204 	    }
    205 #if defined USE_TSEARCH && !defined _LIBC
    206 	  freesa (new_value);
    207 #endif
    208 	}
    209 
    210       if (__environ != last_environ)
    211 	memcpy ((char *) new_environ, (char *) __environ,
    212 		size * sizeof (char *));
    213 
    214       new_environ[size + 1] = NULL;
    215 
    216       last_environ = __environ = new_environ;
    217     }
    218   else if (replace)
    219     {
    220       char *np;
    221 
    222       /* Use the user string if given.  */
    223       if (combined != NULL)
    224 	np = (char *) combined;
    225       else
    226 	{
    227 #ifdef USE_TSEARCH
    228 	  char *new_value;
    229 # ifdef _LIBC
    230 	  new_value = alloca (namelen + 1 + vallen);
    231 	  __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
    232 		     value, vallen);
    233 # else
    234 	  new_value = allocsa (namelen + 1 + vallen);
    235 	  if (new_value == NULL)
    236 	    {
    237 	      __set_errno (ENOMEM);
    238 	      UNLOCK;
    239 	      return -1;
    240 	    }
    241 	  memcpy (new_value, name, namelen);
    242 	  new_value[namelen] = '=';
    243 	  memcpy (&new_value[namelen + 1], value, vallen);
    244 # endif
    245 
    246 	  np = KNOWN_VALUE (new_value);
    247 	  if (np == NULL)
    248 #endif
    249 	    {
    250 	      np = malloc (namelen + 1 + vallen);
    251 	      if (np == NULL)
    252 		{
    253 #if defined USE_TSEARCH && !defined _LIBC
    254 		  freesa (new_value);
    255 #endif
    256 		  __set_errno (ENOMEM);
    257 		  UNLOCK;
    258 		  return -1;
    259 		}
    260 
    261 #ifdef USE_TSEARCH
    262 	      memcpy (np, new_value, namelen + 1 + vallen);
    263 #else
    264 	      memcpy (np, name, namelen);
    265 	      np[namelen] = '=';
    266 	      memcpy (&np[namelen + 1], value, vallen);
    267 #endif
    268 	      /* And remember the value.  */
    269 	      STORE_VALUE (np);
    270 	    }
    271 #if defined USE_TSEARCH && !defined _LIBC
    272 	  freesa (new_value);
    273 #endif
    274 	}
    275 
    276       *ep = np;
    277     }
    278 
    279   UNLOCK;
    280 
    281   return 0;
    282 }
    283 
    284 int
    285 setenv (const char *name, const char *value, int replace)
    286 {
    287   return __add_to_environ (name, value, NULL, replace);
    288 }
    289 
    290 /* The `clearenv' was planned to be added to POSIX.1 but probably
    291    never made it.  Nevertheless the POSIX.9 standard (POSIX bindings
    292    for Fortran 77) requires this function.  */
    293 int
    294 clearenv (void)
    295 {
    296   LOCK;
    297 
    298   if (__environ == last_environ && __environ != NULL)
    299     {
    300       /* We allocated this environment so we can free it.  */
    301       free (__environ);
    302       last_environ = NULL;
    303     }
    304 
    305   /* Clear the environment pointer removes the whole environment.  */
    306   __environ = NULL;
    307 
    308   UNLOCK;
    309 
    310   return 0;
    311 }
    312 
    313 #ifdef _LIBC
    314 static void
    315 free_mem (void)
    316 {
    317   /* Remove all traces.  */
    318   clearenv ();
    319 
    320   /* Now remove the search tree.  */
    321   __tdestroy (known_values, free);
    322   known_values = NULL;
    323 }
    324 text_set_element (__libc_subfreeres, free_mem);
    325 
    326 
    327 # undef setenv
    328 # undef clearenv
    329 weak_alias (__setenv, setenv)
    330 weak_alias (__clearenv, clearenv)
    331 #endif
    332