Blame stdlib/setenv.c

Packit 6c4009
/* Copyright (C) 1992-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#if HAVE_CONFIG_H
Packit 6c4009
# include <config.h>
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Pacify GCC; see the commentary about VALLEN below.  This is needed
Packit 6c4009
   at least through GCC 4.9.2.  Pacify GCC for the entire file, as
Packit 6c4009
   there seems to be no way to pacify GCC selectively, only for the
Packit 6c4009
   place where it's needed.  Do not use DIAG_IGNORE_NEEDS_COMMENT
Packit 6c4009
   here, as it's not defined yet.  */
Packit 6c4009
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
Packit 6c4009
Packit 6c4009
#include <errno.h>
Packit 6c4009
#if !_LIBC
Packit 6c4009
# if !defined errno && !defined HAVE_ERRNO_DECL
Packit 6c4009
extern int errno;
Packit 6c4009
# endif
Packit 6c4009
# define __set_errno(ev) ((errno) = (ev))
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#if _LIBC || HAVE_STDLIB_H
Packit 6c4009
# include <stdlib.h>
Packit 6c4009
#endif
Packit 6c4009
#if _LIBC || HAVE_STRING_H
Packit 6c4009
# include <string.h>
Packit 6c4009
#endif
Packit 6c4009
#if _LIBC || HAVE_UNISTD_H
Packit 6c4009
# include <unistd.h>
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#if !_LIBC
Packit 6c4009
# define __environ	environ
Packit 6c4009
# ifndef HAVE_ENVIRON_DECL
Packit 6c4009
extern char **environ;
Packit 6c4009
# endif
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#if _LIBC
Packit 6c4009
/* This lock protects against simultaneous modifications of `environ'.  */
Packit 6c4009
# include <libc-lock.h>
Packit 6c4009
__libc_lock_define_initialized (static, envlock)
Packit 6c4009
# define LOCK	__libc_lock_lock (envlock)
Packit 6c4009
# define UNLOCK	__libc_lock_unlock (envlock)
Packit 6c4009
#else
Packit 6c4009
# define LOCK
Packit 6c4009
# define UNLOCK
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* In the GNU C library we must keep the namespace clean.  */
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
# define setenv __setenv
Packit 6c4009
# define unsetenv __unsetenv
Packit 6c4009
# define clearenv __clearenv
Packit 6c4009
# define tfind __tfind
Packit 6c4009
# define tsearch __tsearch
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* In the GNU C library implementation we try to be more clever and
Packit 6c4009
   allow arbitrarily many changes of the environment given that the used
Packit 6c4009
   values are from a small set.  Outside glibc this will eat up all
Packit 6c4009
   memory after a while.  */
Packit 6c4009
#if defined _LIBC || (defined HAVE_SEARCH_H && defined HAVE_TSEARCH \
Packit 6c4009
		      && defined __GNUC__)
Packit 6c4009
# define USE_TSEARCH	1
Packit 6c4009
# include <search.h>
Packit 6c4009
Packit 6c4009
/* This is a pointer to the root of the search tree with the known
Packit 6c4009
   values.  */
Packit 6c4009
static void *known_values;
Packit 6c4009
Packit 6c4009
# define KNOWN_VALUE(Str) \
Packit 6c4009
  ({									      \
Packit 6c4009
    void *value = tfind (Str, &known_values, (__compar_fn_t) strcmp);	      \
Packit 6c4009
    value != NULL ? *(char **) value : NULL;				      \
Packit 6c4009
  })
Packit 6c4009
# define STORE_VALUE(Str) \
Packit 6c4009
  tsearch (Str, &known_values, (__compar_fn_t) strcmp)
Packit 6c4009
Packit 6c4009
#else
Packit 6c4009
# undef USE_TSEARCH
Packit 6c4009
Packit 6c4009
# define KNOWN_VALUE(Str) NULL
Packit 6c4009
# define STORE_VALUE(Str) do { } while (0)
Packit 6c4009
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* If this variable is not a null pointer we allocated the current
Packit 6c4009
   environment.  */
Packit 6c4009
static char **last_environ;
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* This function is used by `setenv' and `putenv'.  The difference between
Packit 6c4009
   the two functions is that for the former must create a new string which
Packit 6c4009
   is then placed in the environment, while the argument of `putenv'
Packit 6c4009
   must be used directly.  This is all complicated by the fact that we try
Packit 6c4009
   to reuse values once generated for a `setenv' call since we can never
Packit 6c4009
   free the strings.  */
Packit 6c4009
int
Packit 6c4009
__add_to_environ (const char *name, const char *value, const char *combined,
Packit 6c4009
		  int replace)
Packit 6c4009
{
Packit 6c4009
  char **ep;
Packit 6c4009
  size_t size;
Packit 6c4009
Packit 6c4009
  /* Compute lengths before locking, so that the critical section is
Packit 6c4009
     less of a performance bottleneck.  VALLEN is needed only if
Packit 6c4009
     COMBINED is null (unfortunately GCC is not smart enough to deduce
Packit 6c4009
     this; see the #pragma at the start of this file).  Testing
Packit 6c4009
     COMBINED instead of VALUE causes setenv (..., NULL, ...)  to dump
Packit 6c4009
     core now instead of corrupting memory later.  */
Packit 6c4009
  const size_t namelen = strlen (name);
Packit 6c4009
  size_t vallen;
Packit 6c4009
  if (combined == NULL)
Packit 6c4009
    vallen = strlen (value) + 1;
Packit 6c4009
Packit 6c4009
  LOCK;
Packit 6c4009
Packit 6c4009
  /* We have to get the pointer now that we have the lock and not earlier
Packit 6c4009
     since another thread might have created a new environment.  */
Packit 6c4009
  ep = __environ;
Packit 6c4009
Packit 6c4009
  size = 0;
Packit 6c4009
  if (ep != NULL)
Packit 6c4009
    {
Packit 6c4009
      for (; *ep != NULL; ++ep)
Packit 6c4009
	if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
Packit 6c4009
	  break;
Packit 6c4009
	else
Packit 6c4009
	  ++size;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (ep == NULL || __builtin_expect (*ep == NULL, 1))
Packit 6c4009
    {
Packit 6c4009
      char **new_environ;
Packit 6c4009
Packit 6c4009
      /* We allocated this space; we can extend it.  */
Packit 6c4009
      new_environ = (char **) realloc (last_environ,
Packit 6c4009
				       (size + 2) * sizeof (char *));
Packit 6c4009
      if (new_environ == NULL)
Packit 6c4009
	{
Packit 6c4009
	  UNLOCK;
Packit 6c4009
	  return -1;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (__environ != last_environ)
Packit 6c4009
	memcpy ((char *) new_environ, (char *) __environ,
Packit 6c4009
		size * sizeof (char *));
Packit 6c4009
Packit 6c4009
      new_environ[size] = NULL;
Packit 6c4009
      new_environ[size + 1] = NULL;
Packit 6c4009
      ep = new_environ + size;
Packit 6c4009
Packit 6c4009
      last_environ = __environ = new_environ;
Packit 6c4009
    }
Packit 6c4009
  if (*ep == NULL || replace)
Packit 6c4009
    {
Packit 6c4009
      char *np;
Packit 6c4009
Packit 6c4009
      /* Use the user string if given.  */
Packit 6c4009
      if (combined != NULL)
Packit 6c4009
	np = (char *) combined;
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  const size_t varlen = namelen + 1 + vallen;
Packit 6c4009
#ifdef USE_TSEARCH
Packit 6c4009
	  char *new_value;
Packit 6c4009
	  int use_alloca = __libc_use_alloca (varlen);
Packit 6c4009
	  if (__builtin_expect (use_alloca, 1))
Packit 6c4009
	    new_value = (char *) alloca (varlen);
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      new_value = malloc (varlen);
Packit 6c4009
	      if (new_value == NULL)
Packit 6c4009
		{
Packit 6c4009
		  UNLOCK;
Packit 6c4009
		  return -1;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
# ifdef _LIBC
Packit 6c4009
	  __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
Packit 6c4009
		     value, vallen);
Packit 6c4009
# else
Packit 6c4009
	  memcpy (new_value, name, namelen);
Packit 6c4009
	  new_value[namelen] = '=';
Packit 6c4009
	  memcpy (&new_value[namelen + 1], value, vallen);
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
	  np = KNOWN_VALUE (new_value);
Packit 6c4009
	  if (__glibc_likely (np == NULL))
Packit 6c4009
#endif
Packit 6c4009
	    {
Packit 6c4009
#ifdef USE_TSEARCH
Packit 6c4009
	      if (__glibc_unlikely (! use_alloca))
Packit 6c4009
		np = new_value;
Packit 6c4009
	      else
Packit 6c4009
#endif
Packit 6c4009
		{
Packit 6c4009
		  np = malloc (varlen);
Packit 6c4009
		  if (__glibc_unlikely (np == NULL))
Packit 6c4009
		    {
Packit 6c4009
		      UNLOCK;
Packit 6c4009
		      return -1;
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
#ifdef USE_TSEARCH
Packit 6c4009
		  memcpy (np, new_value, varlen);
Packit 6c4009
#else
Packit 6c4009
		  memcpy (np, name, namelen);
Packit 6c4009
		  np[namelen] = '=';
Packit 6c4009
		  memcpy (&np[namelen + 1], value, vallen);
Packit 6c4009
#endif
Packit 6c4009
		}
Packit 6c4009
	      /* And remember the value.  */
Packit 6c4009
	      STORE_VALUE (np);
Packit 6c4009
	    }
Packit 6c4009
#ifdef USE_TSEARCH
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      if (__glibc_unlikely (! use_alloca))
Packit 6c4009
		free (new_value);
Packit 6c4009
	    }
Packit 6c4009
#endif
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      *ep = np;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  UNLOCK;
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
setenv (const char *name, const char *value, int replace)
Packit 6c4009
{
Packit 6c4009
  if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
Packit 6c4009
    {
Packit 6c4009
      __set_errno (EINVAL);
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return __add_to_environ (name, value, NULL, replace);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
unsetenv (const char *name)
Packit 6c4009
{
Packit 6c4009
  size_t len;
Packit 6c4009
  char **ep;
Packit 6c4009
Packit 6c4009
  if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
Packit 6c4009
    {
Packit 6c4009
      __set_errno (EINVAL);
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  len = strlen (name);
Packit 6c4009
Packit 6c4009
  LOCK;
Packit 6c4009
Packit 6c4009
  ep = __environ;
Packit 6c4009
  if (ep != NULL)
Packit 6c4009
    while (*ep != NULL)
Packit 6c4009
      {
Packit 6c4009
	if (!strncmp (*ep, name, len) && (*ep)[len] == '=')
Packit 6c4009
	  {
Packit 6c4009
	    /* Found it.  Remove this pointer by moving later ones back.  */
Packit 6c4009
	    char **dp = ep;
Packit 6c4009
Packit 6c4009
	    do
Packit 6c4009
		dp[0] = dp[1];
Packit 6c4009
	    while (*dp++);
Packit 6c4009
	    /* Continue the loop in case NAME appears again.  */
Packit 6c4009
	  }
Packit 6c4009
	else
Packit 6c4009
	  ++ep;
Packit 6c4009
      }
Packit 6c4009
Packit 6c4009
  UNLOCK;
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* The `clearenv' was planned to be added to POSIX.1 but probably
Packit 6c4009
   never made it.  Nevertheless the POSIX.9 standard (POSIX bindings
Packit 6c4009
   for Fortran 77) requires this function.  */
Packit 6c4009
int
Packit 6c4009
clearenv (void)
Packit 6c4009
{
Packit 6c4009
  LOCK;
Packit 6c4009
Packit 6c4009
  if (__environ == last_environ && __environ != NULL)
Packit 6c4009
    {
Packit 6c4009
      /* We allocated this environment so we can free it.  */
Packit 6c4009
      free (__environ);
Packit 6c4009
      last_environ = NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Clear the environment pointer removes the whole environment.  */
Packit 6c4009
  __environ = NULL;
Packit 6c4009
Packit 6c4009
  UNLOCK;
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
#ifdef _LIBC
Packit 6c4009
libc_freeres_fn (free_mem)
Packit 6c4009
{
Packit 6c4009
  /* Remove all traces.  */
Packit 6c4009
  clearenv ();
Packit 6c4009
Packit 6c4009
  /* Now remove the search tree.  */
Packit 6c4009
  __tdestroy (known_values, free);
Packit 6c4009
  known_values = NULL;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
# undef setenv
Packit 6c4009
# undef unsetenv
Packit 6c4009
# undef clearenv
Packit 6c4009
weak_alias (__setenv, setenv)
Packit 6c4009
weak_alias (__unsetenv, unsetenv)
Packit 6c4009
weak_alias (__clearenv, clearenv)
Packit 6c4009
#endif