Blame lib/setenv.c

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