(root)/
glibc-2.38/
stdlib/
setenv.c
       1  /* Copyright (C) 1992-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library 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 GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #if HAVE_CONFIG_H
      19  # include <config.h>
      20  #endif
      21  
      22  /* Pacify GCC; see the commentary about VALLEN below.  This is needed
      23     at least through GCC 4.9.2.  Pacify GCC for the entire file, as
      24     there seems to be no way to pacify GCC selectively, only for the
      25     place where it's needed.  Do not use DIAG_IGNORE_NEEDS_COMMENT
      26     here, as it's not defined yet.  */
      27  #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
      28  
      29  #include <errno.h>
      30  #if !_LIBC
      31  # if !defined errno && !defined HAVE_ERRNO_DECL
      32  extern int errno;
      33  # endif
      34  # define __set_errno(ev) ((errno) = (ev))
      35  #endif
      36  
      37  #if _LIBC || HAVE_STDLIB_H
      38  # include <stdlib.h>
      39  #endif
      40  #if _LIBC || HAVE_STRING_H
      41  # include <string.h>
      42  #endif
      43  #if _LIBC || HAVE_UNISTD_H
      44  # include <unistd.h>
      45  #endif
      46  
      47  #if !_LIBC
      48  # define __environ	environ
      49  # ifndef HAVE_ENVIRON_DECL
      50  extern char **environ;
      51  # endif
      52  #endif
      53  
      54  #if _LIBC
      55  /* This lock protects against simultaneous modifications of `environ'.  */
      56  # include <libc-lock.h>
      57  __libc_lock_define_initialized (static, envlock)
      58  # define LOCK	__libc_lock_lock (envlock)
      59  # define UNLOCK	__libc_lock_unlock (envlock)
      60  #else
      61  # define LOCK
      62  # define UNLOCK
      63  #endif
      64  
      65  /* In the GNU C library we must keep the namespace clean.  */
      66  #ifdef _LIBC
      67  # define setenv __setenv
      68  # define unsetenv __unsetenv
      69  # define clearenv __clearenv
      70  # define tfind __tfind
      71  # define tsearch __tsearch
      72  #endif
      73  
      74  /* In the GNU C library implementation we try to be more clever and
      75     allow arbitrarily many changes of the environment given that the used
      76     values are from a small set.  Outside glibc this will eat up all
      77     memory after a while.  */
      78  #if defined _LIBC || (defined HAVE_SEARCH_H && defined HAVE_TSEARCH \
      79  		      && defined __GNUC__)
      80  # define USE_TSEARCH	1
      81  # include <search.h>
      82  
      83  /* This is a pointer to the root of the search tree with the known
      84     values.  */
      85  static void *known_values;
      86  
      87  # define KNOWN_VALUE(Str) \
      88    ({									      \
      89      void *value = tfind (Str, &known_values, (__compar_fn_t) strcmp);	      \
      90      value != NULL ? *(char **) value : NULL;				      \
      91    })
      92  # define STORE_VALUE(Str) \
      93    tsearch (Str, &known_values, (__compar_fn_t) strcmp)
      94  
      95  #else
      96  # undef USE_TSEARCH
      97  
      98  # define KNOWN_VALUE(Str) NULL
      99  # define STORE_VALUE(Str) do { } while (0)
     100  
     101  #endif
     102  
     103  
     104  /* If this variable is not a null pointer we allocated the current
     105     environment.  */
     106  static char **last_environ;
     107  
     108  
     109  /* This function is used by `setenv' and `putenv'.  The difference between
     110     the two functions is that for the former must create a new string which
     111     is then placed in the environment, while the argument of `putenv'
     112     must be used directly.  This is all complicated by the fact that we try
     113     to reuse values once generated for a `setenv' call since we can never
     114     free the strings.  */
     115  int
     116  __add_to_environ (const char *name, const char *value, const char *combined,
     117  		  int replace)
     118  {
     119    char **ep;
     120    size_t size;
     121  
     122    /* Compute lengths before locking, so that the critical section is
     123       less of a performance bottleneck.  VALLEN is needed only if
     124       COMBINED is null (unfortunately GCC is not smart enough to deduce
     125       this; see the #pragma at the start of this file).  Testing
     126       COMBINED instead of VALUE causes setenv (..., NULL, ...)  to dump
     127       core now instead of corrupting memory later.  */
     128    const size_t namelen = strlen (name);
     129    size_t vallen;
     130    if (combined == NULL)
     131      vallen = strlen (value) + 1;
     132  
     133    LOCK;
     134  
     135    /* We have to get the pointer now that we have the lock and not earlier
     136       since another thread might have created a new environment.  */
     137    ep = __environ;
     138  
     139    size = 0;
     140    if (ep != NULL)
     141      {
     142        for (; *ep != NULL; ++ep)
     143  	if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
     144  	  break;
     145  	else
     146  	  ++size;
     147      }
     148  
     149    if (ep == NULL || __builtin_expect (*ep == NULL, 1))
     150      {
     151        char **new_environ;
     152  
     153        /* We allocated this space; we can extend it.  Avoid using the raw
     154  	 reallocated pointer to avoid GCC -Wuse-after-free.  */
     155        uintptr_t ip_last_environ = (uintptr_t)last_environ;
     156        new_environ = (char **) realloc (last_environ,
     157  				       (size + 2) * sizeof (char *));
     158        if (new_environ == NULL)
     159  	{
     160  	  UNLOCK;
     161  	  return -1;
     162  	}
     163  
     164        if ((uintptr_t)__environ != ip_last_environ)
     165  	memcpy ((char *) new_environ, (char *) __environ,
     166  		size * sizeof (char *));
     167  
     168        new_environ[size] = NULL;
     169        new_environ[size + 1] = NULL;
     170        ep = new_environ + size;
     171  
     172        last_environ = __environ = new_environ;
     173      }
     174    if (*ep == NULL || replace)
     175      {
     176        char *np;
     177  
     178        /* Use the user string if given.  */
     179        if (combined != NULL)
     180  	np = (char *) combined;
     181        else
     182  	{
     183  	  const size_t varlen = namelen + 1 + vallen;
     184  #ifdef USE_TSEARCH
     185  	  char *new_value = malloc (varlen);
     186  	  if (new_value == NULL)
     187  	    {
     188  	      UNLOCK;
     189  	      return -1;
     190  	    }
     191  # ifdef _LIBC
     192  	  __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
     193  		     value, vallen);
     194  # else
     195  	  memcpy (new_value, name, namelen);
     196  	  new_value[namelen] = '=';
     197  	  memcpy (&new_value[namelen + 1], value, vallen);
     198  # endif
     199  
     200  	  np = KNOWN_VALUE (new_value);
     201  	  if (__glibc_likely (np == NULL))
     202  #endif
     203  	    {
     204  #ifdef USE_TSEARCH
     205  	      np = new_value;
     206  #endif
     207  	      /* And remember the value.  */
     208  	      STORE_VALUE (np);
     209  	    }
     210  #ifdef USE_TSEARCH
     211  	  else
     212  	    free (new_value);
     213  #endif
     214  	}
     215  
     216        *ep = np;
     217      }
     218  
     219    UNLOCK;
     220  
     221    return 0;
     222  }
     223  
     224  int
     225  setenv (const char *name, const char *value, int replace)
     226  {
     227    if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
     228      {
     229        __set_errno (EINVAL);
     230        return -1;
     231      }
     232  
     233    return __add_to_environ (name, value, NULL, replace);
     234  }
     235  
     236  int
     237  unsetenv (const char *name)
     238  {
     239    size_t len;
     240    char **ep;
     241  
     242    if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
     243      {
     244        __set_errno (EINVAL);
     245        return -1;
     246      }
     247  
     248    len = strlen (name);
     249  
     250    LOCK;
     251  
     252    ep = __environ;
     253    if (ep != NULL)
     254      while (*ep != NULL)
     255        {
     256  	if (!strncmp (*ep, name, len) && (*ep)[len] == '=')
     257  	  {
     258  	    /* Found it.  Remove this pointer by moving later ones back.  */
     259  	    char **dp = ep;
     260  
     261  	    do
     262  		dp[0] = dp[1];
     263  	    while (*dp++);
     264  	    /* Continue the loop in case NAME appears again.  */
     265  	  }
     266  	else
     267  	  ++ep;
     268        }
     269  
     270    UNLOCK;
     271  
     272    return 0;
     273  }
     274  
     275  /* The `clearenv' was planned to be added to POSIX.1 but probably
     276     never made it.  Nevertheless the POSIX.9 standard (POSIX bindings
     277     for Fortran 77) requires this function.  */
     278  int
     279  clearenv (void)
     280  {
     281    LOCK;
     282  
     283    if (__environ == last_environ && __environ != NULL)
     284      {
     285        /* We allocated this environment so we can free it.  */
     286        free (__environ);
     287        last_environ = NULL;
     288      }
     289  
     290    /* Clear the environment pointer removes the whole environment.  */
     291    __environ = NULL;
     292  
     293    UNLOCK;
     294  
     295    return 0;
     296  }
     297  #ifdef _LIBC
     298  void
     299  __libc_setenv_freemem (void)
     300  {
     301    /* Remove all traces.  */
     302    clearenv ();
     303  
     304    /* Now remove the search tree.  */
     305    __tdestroy (known_values, free);
     306    known_values = NULL;
     307  }
     308  
     309  # undef setenv
     310  # undef unsetenv
     311  # undef clearenv
     312  weak_alias (__setenv, setenv)
     313  weak_alias (__unsetenv, unsetenv)
     314  weak_alias (__clearenv, clearenv)
     315  #endif