Blame lib/setenv.c

Packit 8f70b4
/* Copyright (C) 1992, 1995-2003, 2005-2018 Free Software Foundation, Inc.
Packit 8f70b4
   This file is part of the GNU C Library.
Packit 8f70b4
Packit 8f70b4
   This program is free software: you can redistribute it and/or modify
Packit 8f70b4
   it under the terms of the GNU General Public License as published by
Packit 8f70b4
   the Free Software Foundation; either version 3 of the License, or
Packit 8f70b4
   (at your option) any later version.
Packit 8f70b4
Packit 8f70b4
   This program is distributed in the hope that it will be useful,
Packit 8f70b4
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
   GNU General Public License for more details.
Packit 8f70b4
Packit 8f70b4
   You should have received a copy of the GNU General Public License
Packit 8f70b4
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
Packit 8f70b4
Packit 8f70b4
#if !_LIBC
Packit 8f70b4
/* Don't use __attribute__ __nonnull__ in this compilation unit.  Otherwise gcc
Packit 8f70b4
   optimizes away the name == NULL test below.  */
Packit 8f70b4
# define _GL_ARG_NONNULL(params)
Packit 8f70b4
Packit 8f70b4
# define _GL_USE_STDLIB_ALLOC 1
Packit 8f70b4
# include <config.h>
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#include <alloca.h>
Packit 8f70b4
Packit 8f70b4
/* Specification.  */
Packit 8f70b4
#include <stdlib.h>
Packit 8f70b4
Packit 8f70b4
#include <errno.h>
Packit 8f70b4
#ifndef __set_errno
Packit 8f70b4
# define __set_errno(ev) ((errno) = (ev))
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#include <string.h>
Packit 8f70b4
#if _LIBC || HAVE_UNISTD_H
Packit 8f70b4
# include <unistd.h>
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#if !_LIBC
Packit 8f70b4
# include "malloca.h"
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#if _LIBC || !HAVE_SETENV
Packit 8f70b4
Packit 8f70b4
#if !_LIBC
Packit 8f70b4
# define __environ      environ
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#if _LIBC
Packit 8f70b4
/* This lock protects against simultaneous modifications of 'environ'.  */
Packit 8f70b4
# include <bits/libc-lock.h>
Packit 8f70b4
__libc_lock_define_initialized (static, envlock)
Packit 8f70b4
# define LOCK   __libc_lock_lock (envlock)
Packit 8f70b4
# define UNLOCK __libc_lock_unlock (envlock)
Packit 8f70b4
#else
Packit 8f70b4
# define LOCK
Packit 8f70b4
# define UNLOCK
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
/* In the GNU C library we must keep the namespace clean.  */
Packit 8f70b4
#ifdef _LIBC
Packit 8f70b4
# define setenv __setenv
Packit 8f70b4
# define clearenv __clearenv
Packit 8f70b4
# define tfind __tfind
Packit 8f70b4
# define tsearch __tsearch
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
/* In the GNU C library implementation we try to be more clever and
Packit 8f70b4
   allow arbitrarily many changes of the environment given that the used
Packit 8f70b4
   values are from a small set.  Outside glibc this will eat up all
Packit 8f70b4
   memory after a while.  */
Packit 8f70b4
#if defined _LIBC || (defined HAVE_SEARCH_H && defined HAVE_TSEARCH \
Packit 8f70b4
                      && defined __GNUC__)
Packit 8f70b4
# define USE_TSEARCH    1
Packit 8f70b4
# include <search.h>
Packit 8f70b4
typedef int (*compar_fn_t) (const void *, const void *);
Packit 8f70b4
Packit 8f70b4
/* This is a pointer to the root of the search tree with the known
Packit 8f70b4
   values.  */
Packit 8f70b4
static void *known_values;
Packit 8f70b4
Packit 8f70b4
# define KNOWN_VALUE(Str) \
Packit 8f70b4
  ({                                                                          \
Packit 8f70b4
    void *value = tfind (Str, &known_values, (compar_fn_t) strcmp);           \
Packit 8f70b4
    value != NULL ? *(char **) value : NULL;                                  \
Packit 8f70b4
  })
Packit 8f70b4
# define STORE_VALUE(Str) \
Packit 8f70b4
  tsearch (Str, &known_values, (compar_fn_t) strcmp)
Packit 8f70b4
Packit 8f70b4
#else
Packit 8f70b4
# undef USE_TSEARCH
Packit 8f70b4
Packit 8f70b4
# define KNOWN_VALUE(Str) NULL
Packit 8f70b4
# define STORE_VALUE(Str) do { } while (0)
Packit 8f70b4
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
/* If this variable is not a null pointer we allocated the current
Packit 8f70b4
   environment.  */
Packit 8f70b4
static char **last_environ;
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
/* This function is used by 'setenv' and 'putenv'.  The difference between
Packit 8f70b4
   the two functions is that for the former must create a new string which
Packit 8f70b4
   is then placed in the environment, while the argument of 'putenv'
Packit 8f70b4
   must be used directly.  This is all complicated by the fact that we try
Packit 8f70b4
   to reuse values once generated for a 'setenv' call since we can never
Packit 8f70b4
   free the strings.  */
Packit 8f70b4
int
Packit 8f70b4
__add_to_environ (const char *name, const char *value, const char *combined,
Packit 8f70b4
                  int replace)
Packit 8f70b4
{
Packit 8f70b4
  char **ep;
Packit 8f70b4
  size_t size;
Packit 8f70b4
  const size_t namelen = strlen (name);
Packit 8f70b4
  const size_t vallen = value != NULL ? strlen (value) + 1 : 0;
Packit 8f70b4
Packit 8f70b4
  LOCK;
Packit 8f70b4
Packit 8f70b4
  /* We have to get the pointer now that we have the lock and not earlier
Packit 8f70b4
     since another thread might have created a new environment.  */
Packit 8f70b4
  ep = __environ;
Packit 8f70b4
Packit 8f70b4
  size = 0;
Packit 8f70b4
  if (ep != NULL)
Packit 8f70b4
    {
Packit 8f70b4
      for (; *ep != NULL; ++ep)
Packit 8f70b4
        if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
Packit 8f70b4
          break;
Packit 8f70b4
        else
Packit 8f70b4
          ++size;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  if (ep == NULL || *ep == NULL)
Packit 8f70b4
    {
Packit 8f70b4
      char **new_environ;
Packit 8f70b4
#ifdef USE_TSEARCH
Packit 8f70b4
      char *new_value;
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
      /* We allocated this space; we can extend it.  */
Packit 8f70b4
      new_environ =
Packit 8f70b4
        (char **) (last_environ == NULL
Packit 8f70b4
                   ? malloc ((size + 2) * sizeof (char *))
Packit 8f70b4
                   : realloc (last_environ, (size + 2) * sizeof (char *)));
Packit 8f70b4
      if (new_environ == NULL)
Packit 8f70b4
        {
Packit 8f70b4
          /* It's easier to set errno to ENOMEM than to rely on the
Packit 8f70b4
             'malloc-posix' and 'realloc-posix' gnulib modules.  */
Packit 8f70b4
          __set_errno (ENOMEM);
Packit 8f70b4
          UNLOCK;
Packit 8f70b4
          return -1;
Packit 8f70b4
        }
Packit 8f70b4
Packit 8f70b4
      /* If the whole entry is given add it.  */
Packit 8f70b4
      if (combined != NULL)
Packit 8f70b4
        /* We must not add the string to the search tree since it belongs
Packit 8f70b4
           to the user.  */
Packit 8f70b4
        new_environ[size] = (char *) combined;
Packit 8f70b4
      else
Packit 8f70b4
        {
Packit 8f70b4
          /* See whether the value is already known.  */
Packit 8f70b4
#ifdef USE_TSEARCH
Packit 8f70b4
# ifdef _LIBC
Packit 8f70b4
          new_value = (char *) alloca (namelen + 1 + vallen);
Packit 8f70b4
          __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
Packit 8f70b4
                     value, vallen);
Packit 8f70b4
# else
Packit 8f70b4
          new_value = (char *) malloca (namelen + 1 + vallen);
Packit 8f70b4
          if (new_value == NULL)
Packit 8f70b4
            {
Packit 8f70b4
              __set_errno (ENOMEM);
Packit 8f70b4
              UNLOCK;
Packit 8f70b4
              return -1;
Packit 8f70b4
            }
Packit 8f70b4
          memcpy (new_value, name, namelen);
Packit 8f70b4
          new_value[namelen] = '=';
Packit 8f70b4
          memcpy (&new_value[namelen + 1], value, vallen);
Packit 8f70b4
# endif
Packit 8f70b4
Packit 8f70b4
          new_environ[size] = KNOWN_VALUE (new_value);
Packit 8f70b4
          if (new_environ[size] == NULL)
Packit 8f70b4
#endif
Packit 8f70b4
            {
Packit 8f70b4
              new_environ[size] = (char *) malloc (namelen + 1 + vallen);
Packit 8f70b4
              if (new_environ[size] == NULL)
Packit 8f70b4
                {
Packit 8f70b4
#if defined USE_TSEARCH && !defined _LIBC
Packit 8f70b4
                  freea (new_value);
Packit 8f70b4
#endif
Packit 8f70b4
                  __set_errno (ENOMEM);
Packit 8f70b4
                  UNLOCK;
Packit 8f70b4
                  return -1;
Packit 8f70b4
                }
Packit 8f70b4
Packit 8f70b4
#ifdef USE_TSEARCH
Packit 8f70b4
              memcpy (new_environ[size], new_value, namelen + 1 + vallen);
Packit 8f70b4
#else
Packit 8f70b4
              memcpy (new_environ[size], name, namelen);
Packit 8f70b4
              new_environ[size][namelen] = '=';
Packit 8f70b4
              memcpy (&new_environ[size][namelen + 1], value, vallen);
Packit 8f70b4
#endif
Packit 8f70b4
              /* And save the value now.  We cannot do this when we remove
Packit 8f70b4
                 the string since then we cannot decide whether it is a
Packit 8f70b4
                 user string or not.  */
Packit 8f70b4
              STORE_VALUE (new_environ[size]);
Packit 8f70b4
            }
Packit 8f70b4
#if defined USE_TSEARCH && !defined _LIBC
Packit 8f70b4
          freea (new_value);
Packit 8f70b4
#endif
Packit 8f70b4
        }
Packit 8f70b4
Packit 8f70b4
      if (__environ != last_environ)
Packit 8f70b4
        memcpy ((char *) new_environ, (char *) __environ,
Packit 8f70b4
                size * sizeof (char *));
Packit 8f70b4
Packit 8f70b4
      new_environ[size + 1] = NULL;
Packit 8f70b4
Packit 8f70b4
      last_environ = __environ = new_environ;
Packit 8f70b4
    }
Packit 8f70b4
  else if (replace)
Packit 8f70b4
    {
Packit 8f70b4
      char *np;
Packit 8f70b4
Packit 8f70b4
      /* Use the user string if given.  */
Packit 8f70b4
      if (combined != NULL)
Packit 8f70b4
        np = (char *) combined;
Packit 8f70b4
      else
Packit 8f70b4
        {
Packit 8f70b4
#ifdef USE_TSEARCH
Packit 8f70b4
          char *new_value;
Packit 8f70b4
# ifdef _LIBC
Packit 8f70b4
          new_value = alloca (namelen + 1 + vallen);
Packit 8f70b4
          __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
Packit 8f70b4
                     value, vallen);
Packit 8f70b4
# else
Packit 8f70b4
          new_value = malloca (namelen + 1 + vallen);
Packit 8f70b4
          if (new_value == NULL)
Packit 8f70b4
            {
Packit 8f70b4
              __set_errno (ENOMEM);
Packit 8f70b4
              UNLOCK;
Packit 8f70b4
              return -1;
Packit 8f70b4
            }
Packit 8f70b4
          memcpy (new_value, name, namelen);
Packit 8f70b4
          new_value[namelen] = '=';
Packit 8f70b4
          memcpy (&new_value[namelen + 1], value, vallen);
Packit 8f70b4
# endif
Packit 8f70b4
Packit 8f70b4
          np = KNOWN_VALUE (new_value);
Packit 8f70b4
          if (np == NULL)
Packit 8f70b4
#endif
Packit 8f70b4
            {
Packit 8f70b4
              np = (char *) malloc (namelen + 1 + vallen);
Packit 8f70b4
              if (np == NULL)
Packit 8f70b4
                {
Packit 8f70b4
#if defined USE_TSEARCH && !defined _LIBC
Packit 8f70b4
                  freea (new_value);
Packit 8f70b4
#endif
Packit 8f70b4
                  __set_errno (ENOMEM);
Packit 8f70b4
                  UNLOCK;
Packit 8f70b4
                  return -1;
Packit 8f70b4
                }
Packit 8f70b4
Packit 8f70b4
#ifdef USE_TSEARCH
Packit 8f70b4
              memcpy (np, new_value, namelen + 1 + vallen);
Packit 8f70b4
#else
Packit 8f70b4
              memcpy (np, name, namelen);
Packit 8f70b4
              np[namelen] = '=';
Packit 8f70b4
              memcpy (&np[namelen + 1], value, vallen);
Packit 8f70b4
#endif
Packit 8f70b4
              /* And remember the value.  */
Packit 8f70b4
              STORE_VALUE (np);
Packit 8f70b4
            }
Packit 8f70b4
#if defined USE_TSEARCH && !defined _LIBC
Packit 8f70b4
          freea (new_value);
Packit 8f70b4
#endif
Packit 8f70b4
        }
Packit 8f70b4
Packit 8f70b4
      *ep = np;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  UNLOCK;
Packit 8f70b4
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
setenv (const char *name, const char *value, int replace)
Packit 8f70b4
{
Packit 8f70b4
  if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
Packit 8f70b4
    {
Packit 8f70b4
      __set_errno (EINVAL);
Packit 8f70b4
      return -1;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  return __add_to_environ (name, value, NULL, replace);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/* The 'clearenv' was planned to be added to POSIX.1 but probably
Packit 8f70b4
   never made it.  Nevertheless the POSIX.9 standard (POSIX bindings
Packit 8f70b4
   for Fortran 77) requires this function.  */
Packit 8f70b4
int
Packit 8f70b4
clearenv (void)
Packit 8f70b4
{
Packit 8f70b4
  LOCK;
Packit 8f70b4
Packit 8f70b4
  if (__environ == last_environ && __environ != NULL)
Packit 8f70b4
    {
Packit 8f70b4
      /* We allocated this environment so we can free it.  */
Packit 8f70b4
      free (__environ);
Packit 8f70b4
      last_environ = NULL;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  /* Clear the environment pointer removes the whole environment.  */
Packit 8f70b4
  __environ = NULL;
Packit 8f70b4
Packit 8f70b4
  UNLOCK;
Packit 8f70b4
Packit 8f70b4
  return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#ifdef _LIBC
Packit 8f70b4
static void
Packit 8f70b4
free_mem (void)
Packit 8f70b4
{
Packit 8f70b4
  /* Remove all traces.  */
Packit 8f70b4
  clearenv ();
Packit 8f70b4
Packit 8f70b4
  /* Now remove the search tree.  */
Packit 8f70b4
  __tdestroy (known_values, free);
Packit 8f70b4
  known_values = NULL;
Packit 8f70b4
}
Packit 8f70b4
text_set_element (__libc_subfreeres, free_mem);
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
# undef setenv
Packit 8f70b4
# undef clearenv
Packit 8f70b4
weak_alias (__setenv, setenv)
Packit 8f70b4
weak_alias (__clearenv, clearenv)
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#endif /* _LIBC || !HAVE_SETENV */
Packit 8f70b4
Packit 8f70b4
/* The rest of this file is called into use when replacing an existing
Packit 8f70b4
   but buggy setenv.  Known bugs include failure to diagnose invalid
Packit 8f70b4
   name, and consuming a leading '=' from value.  */
Packit 8f70b4
#if HAVE_SETENV
Packit 8f70b4
Packit 8f70b4
# undef setenv
Packit 8f70b4
# if !HAVE_DECL_SETENV
Packit 8f70b4
extern int setenv (const char *, const char *, int);
Packit 8f70b4
# endif
Packit 8f70b4
# define STREQ(a, b) (strcmp (a, b) == 0)
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
rpl_setenv (const char *name, const char *value, int replace)
Packit 8f70b4
{
Packit 8f70b4
  int result;
Packit 8f70b4
  if (!name || !*name || strchr (name, '='))
Packit 8f70b4
    {
Packit 8f70b4
      errno = EINVAL;
Packit 8f70b4
      return -1;
Packit 8f70b4
    }
Packit 8f70b4
  /* Call the real setenv even if replace is 0, in case implementation
Packit 8f70b4
     has underlying data to update, such as when environ changes.  */
Packit 8f70b4
  result = setenv (name, value, replace);
Packit 8f70b4
  if (result == 0 && replace && *value == '=')
Packit 8f70b4
    {
Packit 8f70b4
      char *tmp = getenv (name);
Packit 8f70b4
      if (!STREQ (tmp, value))
Packit 8f70b4
        {
Packit 8f70b4
          int saved_errno;
Packit 8f70b4
          size_t len = strlen (value);
Packit 8f70b4
          tmp = malloca (len + 2);
Packit 8f70b4
          /* Since leading '=' is eaten, double it up.  */
Packit 8f70b4
          *tmp = '=';
Packit 8f70b4
          memcpy (tmp + 1, value, len + 1);
Packit 8f70b4
          result = setenv (name, tmp, replace);
Packit 8f70b4
          saved_errno = errno;
Packit 8f70b4
          freea (tmp);
Packit 8f70b4
          errno = saved_errno;
Packit 8f70b4
        }
Packit 8f70b4
    }
Packit 8f70b4
  return result;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#endif /* HAVE_SETENV */