Blame lib/setenv.c

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