Blame lib/setenv.c

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