setenv.c 10.5 KB
Newer Older
Paul Eggert's avatar
Paul Eggert committed
1
/* Copyright (C) 1992, 1995-2003, 2005-2016 Free Software Foundation, Inc.
Bruno Haible's avatar
Bruno Haible committed
2
   This file is part of the GNU C Library.
Jim Meyering's avatar
.  
Jim Meyering committed
3

4
   This program is free software: you can redistribute it and/or modify
5
   it under the terms of the GNU General Public License as published by
6 7
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.
Jim Meyering's avatar
.  
Jim Meyering committed
8

9
   This program is distributed in the hope that it will be useful,
Bruno Haible's avatar
Bruno Haible committed
10
   but WITHOUT ANY WARRANTY; without even the implied warranty of
11 12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
Jim Meyering's avatar
.  
Jim Meyering committed
13

14 15
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Jim Meyering's avatar
.  
Jim Meyering committed
16

17
#if !_LIBC
18 19 20 21
/* Don't use __attribute__ __nonnull__ in this compilation unit.  Otherwise gcc
   optimizes away the name == NULL test below.  */
# define _GL_ARG_NONNULL(params)

22
# define _GL_USE_STDLIB_ALLOC 1
Jim Meyering's avatar
Jim Meyering committed
23
# include <config.h>
Jim Meyering's avatar
.  
Jim Meyering committed
24
#endif
25

26
#include <alloca.h>
Bruno Haible's avatar
Bruno Haible committed
27

28 29 30
/* Specification.  */
#include <stdlib.h>

Jim Meyering's avatar
.  
Jim Meyering committed
31
#include <errno.h>
Paul Eggert's avatar
Paul Eggert committed
32
#ifndef __set_errno
Bruno Haible's avatar
Bruno Haible committed
33 34
# define __set_errno(ev) ((errno) = (ev))
#endif
Jim Meyering's avatar
.  
Jim Meyering committed
35

36
#include <string.h>
37 38 39
#if _LIBC || HAVE_UNISTD_H
# include <unistd.h>
#endif
Jim Meyering's avatar
.  
Jim Meyering committed
40

41
#if !_LIBC
42
# include "malloca.h"
Bruno Haible's avatar
Bruno Haible committed
43 44
#endif

45 46
#if _LIBC || !HAVE_SETENV

Bruno Haible's avatar
Bruno Haible committed
47
#if !_LIBC
48
# define __environ      environ
Bruno Haible's avatar
Bruno Haible committed
49 50 51
#endif

#if _LIBC
52
/* This lock protects against simultaneous modifications of 'environ'.  */
Bruno Haible's avatar
Bruno Haible committed
53 54
# include <bits/libc-lock.h>
__libc_lock_define_initialized (static, envlock)
55 56
# define LOCK   __libc_lock_lock (envlock)
# define UNLOCK __libc_lock_unlock (envlock)
Bruno Haible's avatar
Bruno Haible committed
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
#else
# define LOCK
# define UNLOCK
#endif

/* In the GNU C library we must keep the namespace clean.  */
#ifdef _LIBC
# define setenv __setenv
# define clearenv __clearenv
# define tfind __tfind
# define tsearch __tsearch
#endif

/* In the GNU C library implementation we try to be more clever and
   allow arbitrarily many changes of the environment given that the used
   values are from a small set.  Outside glibc this will eat up all
   memory after a while.  */
#if defined _LIBC || (defined HAVE_SEARCH_H && defined HAVE_TSEARCH \
75 76
                      && defined __GNUC__)
# define USE_TSEARCH    1
Bruno Haible's avatar
Bruno Haible committed
77 78 79 80 81 82 83 84
# include <search.h>
typedef int (*compar_fn_t) (const void *, const void *);

/* This is a pointer to the root of the search tree with the known
   values.  */
static void *known_values;

# define KNOWN_VALUE(Str) \
85 86 87
  ({                                                                          \
    void *value = tfind (Str, &known_values, (compar_fn_t) strcmp);           \
    value != NULL ? *(char **) value : NULL;                                  \
Bruno Haible's avatar
Bruno Haible committed
88 89 90 91 92 93 94 95 96 97
  })
# define STORE_VALUE(Str) \
  tsearch (Str, &known_values, (compar_fn_t) strcmp)

#else
# undef USE_TSEARCH

# define KNOWN_VALUE(Str) NULL
# define STORE_VALUE(Str) do { } while (0)

Jim Meyering's avatar
.  
Jim Meyering committed
98 99
#endif

Bruno Haible's avatar
Bruno Haible committed
100 101 102 103 104 105

/* If this variable is not a null pointer we allocated the current
   environment.  */
static char **last_environ;


106
/* This function is used by 'setenv' and 'putenv'.  The difference between
Bruno Haible's avatar
Bruno Haible committed
107
   the two functions is that for the former must create a new string which
108
   is then placed in the environment, while the argument of 'putenv'
Bruno Haible's avatar
Bruno Haible committed
109
   must be used directly.  This is all complicated by the fact that we try
110
   to reuse values once generated for a 'setenv' call since we can never
Bruno Haible's avatar
Bruno Haible committed
111
   free the strings.  */
Jim Meyering's avatar
.  
Jim Meyering committed
112
int
Bruno Haible's avatar
Bruno Haible committed
113
__add_to_environ (const char *name, const char *value, const char *combined,
114
                  int replace)
Jim Meyering's avatar
.  
Jim Meyering committed
115
{
116 117
  char **ep;
  size_t size;
Jim Meyering's avatar
.  
Jim Meyering committed
118
  const size_t namelen = strlen (name);
Bruno Haible's avatar
Bruno Haible committed
119 120 121 122 123 124 125
  const size_t vallen = value != NULL ? strlen (value) + 1 : 0;

  LOCK;

  /* We have to get the pointer now that we have the lock and not earlier
     since another thread might have created a new environment.  */
  ep = __environ;
Jim Meyering's avatar
.  
Jim Meyering committed
126 127

  size = 0;
Bruno Haible's avatar
Bruno Haible committed
128 129 130
  if (ep != NULL)
    {
      for (; *ep != NULL; ++ep)
131 132 133 134
        if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
          break;
        else
          ++size;
Bruno Haible's avatar
Bruno Haible committed
135
    }
Jim Meyering's avatar
.  
Jim Meyering committed
136

Bruno Haible's avatar
Bruno Haible committed
137
  if (ep == NULL || *ep == NULL)
Jim Meyering's avatar
.  
Jim Meyering committed
138 139
    {
      char **new_environ;
Bruno Haible's avatar
Bruno Haible committed
140 141 142
#ifdef USE_TSEARCH
      char *new_value;
#endif
Jim Meyering's avatar
.  
Jim Meyering committed
143

Bruno Haible's avatar
Bruno Haible committed
144 145
      /* We allocated this space; we can extend it.  */
      new_environ =
146 147 148
        (char **) (last_environ == NULL
                   ? malloc ((size + 2) * sizeof (char *))
                   : realloc (last_environ, (size + 2) * sizeof (char *)));
Jim Meyering's avatar
.  
Jim Meyering committed
149
      if (new_environ == NULL)
150
        {
151 152 153
          /* It's easier to set errno to ENOMEM than to rely on the
             'malloc-posix' and 'realloc-posix' gnulib modules.  */
          __set_errno (ENOMEM);
154 155 156
          UNLOCK;
          return -1;
        }
Jim Meyering's avatar
.  
Jim Meyering committed
157

Bruno Haible's avatar
Bruno Haible committed
158 159
      /* If the whole entry is given add it.  */
      if (combined != NULL)
160 161 162
        /* We must not add the string to the search tree since it belongs
           to the user.  */
        new_environ[size] = (char *) combined;
Bruno Haible's avatar
Bruno Haible committed
163
      else
164 165
        {
          /* See whether the value is already known.  */
Bruno Haible's avatar
Bruno Haible committed
166 167
#ifdef USE_TSEARCH
# ifdef _LIBC
168 169 170
          new_value = (char *) alloca (namelen + 1 + vallen);
          __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
                     value, vallen);
Bruno Haible's avatar
Bruno Haible committed
171
# else
172 173 174 175 176 177 178 179 180 181
          new_value = (char *) malloca (namelen + 1 + vallen);
          if (new_value == NULL)
            {
              __set_errno (ENOMEM);
              UNLOCK;
              return -1;
            }
          memcpy (new_value, name, namelen);
          new_value[namelen] = '=';
          memcpy (&new_value[namelen + 1], value, vallen);
Bruno Haible's avatar
Bruno Haible committed
182 183
# endif

184 185
          new_environ[size] = KNOWN_VALUE (new_value);
          if (new_environ[size] == NULL)
Bruno Haible's avatar
Bruno Haible committed
186
#endif
187 188 189 190
            {
              new_environ[size] = (char *) malloc (namelen + 1 + vallen);
              if (new_environ[size] == NULL)
                {
191
#if defined USE_TSEARCH && !defined _LIBC
192
                  freea (new_value);
Bruno Haible's avatar
Bruno Haible committed
193
#endif
194 195 196 197
                  __set_errno (ENOMEM);
                  UNLOCK;
                  return -1;
                }
Bruno Haible's avatar
Bruno Haible committed
198 199

#ifdef USE_TSEARCH
200
              memcpy (new_environ[size], new_value, namelen + 1 + vallen);
Bruno Haible's avatar
Bruno Haible committed
201
#else
202 203 204
              memcpy (new_environ[size], name, namelen);
              new_environ[size][namelen] = '=';
              memcpy (&new_environ[size][namelen + 1], value, vallen);
Bruno Haible's avatar
Bruno Haible committed
205
#endif
206 207 208 209 210
              /* And save the value now.  We cannot do this when we remove
                 the string since then we cannot decide whether it is a
                 user string or not.  */
              STORE_VALUE (new_environ[size]);
            }
211
#if defined USE_TSEARCH && !defined _LIBC
212
          freea (new_value);
Bruno Haible's avatar
Bruno Haible committed
213
#endif
214
        }
Bruno Haible's avatar
Bruno Haible committed
215

Jim Meyering's avatar
.  
Jim Meyering committed
216
      if (__environ != last_environ)
217 218
        memcpy ((char *) new_environ, (char *) __environ,
                size * sizeof (char *));
Jim Meyering's avatar
.  
Jim Meyering committed
219 220 221 222 223 224 225

      new_environ[size + 1] = NULL;

      last_environ = __environ = new_environ;
    }
  else if (replace)
    {
Bruno Haible's avatar
Bruno Haible committed
226 227 228 229
      char *np;

      /* Use the user string if given.  */
      if (combined != NULL)
230
        np = (char *) combined;
Bruno Haible's avatar
Bruno Haible committed
231
      else
232
        {
Bruno Haible's avatar
Bruno Haible committed
233
#ifdef USE_TSEARCH
234
          char *new_value;
Bruno Haible's avatar
Bruno Haible committed
235
# ifdef _LIBC
236 237 238
          new_value = alloca (namelen + 1 + vallen);
          __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
                     value, vallen);
Bruno Haible's avatar
Bruno Haible committed
239
# else
240 241 242 243 244 245 246 247 248 249
          new_value = malloca (namelen + 1 + vallen);
          if (new_value == NULL)
            {
              __set_errno (ENOMEM);
              UNLOCK;
              return -1;
            }
          memcpy (new_value, name, namelen);
          new_value[namelen] = '=';
          memcpy (&new_value[namelen + 1], value, vallen);
Bruno Haible's avatar
Bruno Haible committed
250 251
# endif

252 253
          np = KNOWN_VALUE (new_value);
          if (np == NULL)
Bruno Haible's avatar
Bruno Haible committed
254
#endif
255
            {
256
              np = (char *) malloc (namelen + 1 + vallen);
257 258
              if (np == NULL)
                {
259
#if defined USE_TSEARCH && !defined _LIBC
260
                  freea (new_value);
Bruno Haible's avatar
Bruno Haible committed
261
#endif
262 263 264 265
                  __set_errno (ENOMEM);
                  UNLOCK;
                  return -1;
                }
Bruno Haible's avatar
Bruno Haible committed
266 267

#ifdef USE_TSEARCH
268
              memcpy (np, new_value, namelen + 1 + vallen);
Bruno Haible's avatar
Bruno Haible committed
269
#else
270 271 272
              memcpy (np, name, namelen);
              np[namelen] = '=';
              memcpy (&np[namelen + 1], value, vallen);
Bruno Haible's avatar
Bruno Haible committed
273
#endif
274 275 276
              /* And remember the value.  */
              STORE_VALUE (np);
            }
277
#if defined USE_TSEARCH && !defined _LIBC
278
          freea (new_value);
Bruno Haible's avatar
Bruno Haible committed
279
#endif
280
        }
Bruno Haible's avatar
Bruno Haible committed
281 282

      *ep = np;
Jim Meyering's avatar
.  
Jim Meyering committed
283 284
    }

Bruno Haible's avatar
Bruno Haible committed
285 286
  UNLOCK;

Jim Meyering's avatar
.  
Jim Meyering committed
287 288 289
  return 0;
}

Bruno Haible's avatar
Bruno Haible committed
290 291
int
setenv (const char *name, const char *value, int replace)
Jim Meyering's avatar
.  
Jim Meyering committed
292
{
293 294 295 296 297 298
  if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
    {
      __set_errno (EINVAL);
      return -1;
    }

Bruno Haible's avatar
Bruno Haible committed
299
  return __add_to_environ (name, value, NULL, replace);
Jim Meyering's avatar
.  
Jim Meyering committed
300
}
Bruno Haible's avatar
Bruno Haible committed
301

302
/* The 'clearenv' was planned to be added to POSIX.1 but probably
Bruno Haible's avatar
Bruno Haible committed
303 304 305
   never made it.  Nevertheless the POSIX.9 standard (POSIX bindings
   for Fortran 77) requires this function.  */
int
Paul Eggert's avatar
Paul Eggert committed
306
clearenv (void)
Bruno Haible's avatar
Bruno Haible committed
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
{
  LOCK;

  if (__environ == last_environ && __environ != NULL)
    {
      /* We allocated this environment so we can free it.  */
      free (__environ);
      last_environ = NULL;
    }

  /* Clear the environment pointer removes the whole environment.  */
  __environ = NULL;

  UNLOCK;

  return 0;
}

#ifdef _LIBC
static void
free_mem (void)
{
  /* Remove all traces.  */
  clearenv ();

  /* Now remove the search tree.  */
  __tdestroy (known_values, free);
  known_values = NULL;
}
text_set_element (__libc_subfreeres, free_mem);


# undef setenv
# undef clearenv
weak_alias (__setenv, setenv)
weak_alias (__clearenv, clearenv)
#endif
344 345

#endif /* _LIBC || !HAVE_SETENV */
346 347 348 349 350 351 352

/* The rest of this file is called into use when replacing an existing
   but buggy setenv.  Known bugs include failure to diagnose invalid
   name, and consuming a leading '=' from value.  */
#if HAVE_SETENV

# undef setenv
353 354 355
# if !HAVE_DECL_SETENV
extern int setenv (const char *, const char *, int);
# endif
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
# define STREQ(a, b) (strcmp (a, b) == 0)

int
rpl_setenv (const char *name, const char *value, int replace)
{
  int result;
  if (!name || !*name || strchr (name, '='))
    {
      errno = EINVAL;
      return -1;
    }
  /* Call the real setenv even if replace is 0, in case implementation
     has underlying data to update, such as when environ changes.  */
  result = setenv (name, value, replace);
  if (result == 0 && replace && *value == '=')
    {
      char *tmp = getenv (name);
      if (!STREQ (tmp, value))
        {
          int saved_errno;
          size_t len = strlen (value);
          tmp = malloca (len + 2);
          /* Since leading '=' is eaten, double it up.  */
          *tmp = '=';
          memcpy (tmp + 1, value, len + 1);
          result = setenv (name, tmp, replace);
          saved_errno = errno;
          freea (tmp);
          errno = saved_errno;
        }
    }
  return result;
}

#endif /* HAVE_SETENV */