]> git.ipfire.org Git - thirdparty/glibc.git/blame - stdlib/setenv.c
manual: Remove '.info' suffix in manual names passed to @ref [BZ #32962].
[thirdparty/glibc.git] / stdlib / setenv.c
CommitLineData
26420023 1/* Copyright (C) 1992-2025 Free Software Foundation, Inc.
f0e44959 2 This file is part of the GNU C Library.
28f540f4 3
f0e44959 4 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
28f540f4 8
f0e44959
UD
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41bdb6e2 12 Lesser General Public License for more details.
28f540f4 13
41bdb6e2 14 You should have received a copy of the GNU Lesser General Public
59ba27a6 15 License along with the GNU C Library; if not, see
5a82c748 16 <https://www.gnu.org/licenses/>. */
28f540f4 17
f0e44959
UD
18#if HAVE_CONFIG_H
19# include <config.h>
196980f5
RM
20#endif
21
7a61e7f5
FW
22#include <assert.h>
23#include <setenv.h>
24
03c1e456
PE
25/* Pacify GCC; see the commentary about VALLEN below. This is needed
26 at least through GCC 4.9.2. Pacify GCC for the entire file, as
27 there seems to be no way to pacify GCC selectively, only for the
28 place where it's needed. Do not use DIAG_IGNORE_NEEDS_COMMENT
29 here, as it's not defined yet. */
f1d237df 30#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
03c1e456 31
196980f5 32#include <errno.h>
61c162b5 33#if !_LIBC
ff152e3f 34# if !defined errno && !defined HAVE_ERRNO_DECL
61c162b5
UD
35extern int errno;
36# endif
37# define __set_errno(ev) ((errno) = (ev))
38#endif
196980f5
RM
39
40#if _LIBC || HAVE_STDLIB_H
f0e44959 41# include <stdlib.h>
196980f5
RM
42#endif
43#if _LIBC || HAVE_STRING_H
f0e44959 44# include <string.h>
196980f5
RM
45#endif
46#if _LIBC || HAVE_UNISTD_H
f0e44959 47# include <unistd.h>
196980f5 48#endif
28f540f4 49
61c162b5 50#if !_LIBC
f0e44959 51# define __environ environ
61c162b5
UD
52# ifndef HAVE_ENVIRON_DECL
53extern char **environ;
54# endif
28f540f4
RM
55#endif
56
61c162b5 57#if _LIBC
9e0f4072 58/* This lock protects against simultaneous modifications of `environ'. */
ec999b8e 59# include <libc-lock.h>
9e0f4072 60__libc_lock_define_initialized (static, envlock)
f0e44959
UD
61# define LOCK __libc_lock_lock (envlock)
62# define UNLOCK __libc_lock_unlock (envlock)
9e0f4072 63#else
f0e44959
UD
64# define LOCK
65# define UNLOCK
9e0f4072
RM
66#endif
67
3e5f5557
UD
68/* In the GNU C library we must keep the namespace clean. */
69#ifdef _LIBC
ff152e3f
UD
70# define setenv __setenv
71# define unsetenv __unsetenv
3e5f5557 72# define clearenv __clearenv
b17277cf
UD
73# define tfind __tfind
74# define tsearch __tsearch
3e5f5557
UD
75#endif
76
ff152e3f 77/* In the GNU C library implementation we try to be more clever and
49c091e5 78 allow arbitrarily many changes of the environment given that the used
ff152e3f
UD
79 values are from a small set. Outside glibc this will eat up all
80 memory after a while. */
a709dd43
UD
81#if defined _LIBC || (defined HAVE_SEARCH_H && defined HAVE_TSEARCH \
82 && defined __GNUC__)
ff152e3f
UD
83# define USE_TSEARCH 1
84# include <search.h>
85
86/* This is a pointer to the root of the search tree with the known
87 values. */
88static void *known_values;
89
b17277cf
UD
90# define KNOWN_VALUE(Str) \
91 ({ \
a709dd43
UD
92 void *value = tfind (Str, &known_values, (__compar_fn_t) strcmp); \
93 value != NULL ? *(char **) value : NULL; \
b17277cf
UD
94 })
95# define STORE_VALUE(Str) \
a709dd43 96 tsearch (Str, &known_values, (__compar_fn_t) strcmp)
ff152e3f
UD
97
98#else
99# undef USE_TSEARCH
100
101# define KNOWN_VALUE(Str) NULL
102# define STORE_VALUE(Str) do { } while (0)
103
104#endif
105
7a61e7f5
FW
106/* Allocate a new environment array and put it o the
107 __environ_array_list. Returns NULL on memory allocation
108 failure. */
109static struct environ_array *
110__environ_new_array (size_t required_size)
111{
112 /* No backing array yet, or insufficient room. */
113 size_t new_size;
114 if (__environ_array_list == NULL
115 || __environ_array_list->allocated * 2 < required_size)
116 /* Add some unused space for future growth. */
117 new_size = required_size + 16;
118 else
119 new_size = __environ_array_list->allocated * 2;
120
12b4a1fc
FW
121 /* Zero-initialize everything, so that getenv can only
122 observe valid or null pointers. */
123 char **new_array = calloc (new_size, sizeof (*new_array));
124 if (new_array == NULL)
125 return NULL;
126
127 struct environ_array *target_array = malloc (sizeof (*target_array));
128 if (target_array == NULL)
7a61e7f5 129 {
12b4a1fc 130 free (new_array);
7a61e7f5
FW
131 return NULL;
132 }
3e5f5557 133
7a61e7f5 134 target_array->allocated = new_size;
12b4a1fc 135 target_array->array = new_array;
7a61e7f5
FW
136 assert (new_size >= target_array->allocated);
137
138 /* Put it onto the list. */
139 target_array->next = __environ_array_list;
140 __environ_array_list = target_array;
141 return target_array;
142}
f0e44959 143
28f540f4 144int
f63f2bfd
JM
145__add_to_environ (const char *name, const char *value, const char *combined,
146 int replace)
28f540f4 147{
03c1e456
PE
148 /* Compute lengths before locking, so that the critical section is
149 less of a performance bottleneck. VALLEN is needed only if
150 COMBINED is null (unfortunately GCC is not smart enough to deduce
151 this; see the #pragma at the start of this file). Testing
152 COMBINED instead of VALUE causes setenv (..., NULL, ...) to dump
153 core now instead of corrupting memory later. */
196980f5 154 const size_t namelen = strlen (name);
03c1e456
PE
155 size_t vallen;
156 if (combined == NULL)
157 vallen = strlen (value) + 1;
28f540f4 158
9e0f4072
RM
159 LOCK;
160
7195db12 161 /* We have to get the pointer now that we have the lock and not earlier
7a61e7f5
FW
162 since another thread might have created a new environment. */
163 char **start_environ = atomic_load_relaxed (&__environ);
164 char **ep = start_environ;
7195db12 165
7a61e7f5
FW
166 /* This gets written to __environ in the end. */
167 char **result_environ = start_environ;
168
169 /* Size of the environment if *ep == NULL. */
96ff4937 170 if (ep != NULL)
7a61e7f5
FW
171 for (; *ep != NULL; ++ep)
172 if (strncmp (*ep, name, namelen) == 0 && (*ep)[namelen] == '=')
173 break;
196980f5 174
7a61e7f5 175 if (ep == NULL || __glibc_likely (*ep == NULL))
28f540f4 176 {
7a61e7f5
FW
177 /* The scanning loop above reached the end of the environment.
178 Add a new string to it. */
179 replace = true;
180
181 /* + 2 for the new entry and the terminating NULL. */
182 size_t required_size = (ep - start_environ) + 2;
183 if (__environ_is_from_array_list (start_environ)
184 && required_size <= __environ_array_list->allocated)
185 /* The __environ array is ours, and we have room in it. We
186 can use ep as-is. Add a null terminator in case current
187 usage is less than previous usage. */
188 ep[1] = NULL;
189 else
9e0f4072 190 {
36fcdfbb
FW
191 /* We cannot use __environ as is and need to copy over the
192 __environ contents into an array managed via
193 __environ_array_list. */
194
195 struct environ_array *target_array;
196 if (__environ_array_list != NULL
197 && required_size <= __environ_array_list->allocated)
198 /* Existing array has enough room. Contents is copied below. */
199 target_array = __environ_array_list;
b62759db
FW
200 else
201 {
36fcdfbb
FW
202 /* Allocate a new array. */
203 target_array = __environ_new_array (required_size);
204 if (target_array == NULL)
b62759db
FW
205 {
206 UNLOCK;
207 return -1;
208 }
7a61e7f5 209 }
28f540f4 210
36fcdfbb
FW
211 /* Copy over the __environ array contents. This forward
212 copy slides backwards part of the array if __environ
213 points into target_array->array. This happens if an
214 application makes an assignment like:
215
216 environ = &environ[1];
217
218 The forward copy avoids clobbering values that still
219 needing copying. This code handles the case
220 start_environ == ep == NULL, too. */
221 size_t i;
222 for (i = 0; start_environ + i < ep; ++i)
223 /* Regular store because unless there has been direct
224 manipulation of the environment, target_array is still
225 a private copy. */
226 target_array->array[i] = atomic_load_relaxed (start_environ + i);
227
7a61e7f5 228 /* This is the new place where we should add the element. */
36fcdfbb 229 ep = target_array->array + i;
7a61e7f5
FW
230
231 /* Add the null terminator in case there was a pointer there
232 previously. */
233 ep[1] = NULL;
36fcdfbb
FW
234
235 /* And __environ should be repointed to our array. */
12b4a1fc 236 result_environ = target_array->array;
7a61e7f5 237 }
28f540f4 238 }
7a61e7f5
FW
239
240 if (replace || *ep == NULL)
28f540f4 241 {
cb37d842 242 char *np;
ff152e3f 243
de4a40d0
UD
244 /* Use the user string if given. */
245 if (combined != NULL)
246 np = (char *) combined;
247 else
248 {
c63bfa79 249 const size_t varlen = namelen + 1 + vallen;
ff152e3f 250#ifdef USE_TSEARCH
9401024e
JST
251 char *new_value = malloc (varlen);
252 if (new_value == NULL)
c63bfa79 253 {
9401024e
JST
254 UNLOCK;
255 return -1;
c63bfa79 256 }
ff152e3f 257# ifdef _LIBC
de4a40d0
UD
258 __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
259 value, vallen);
ff152e3f 260# else
de4a40d0
UD
261 memcpy (new_value, name, namelen);
262 new_value[namelen] = '=';
263 memcpy (&new_value[namelen + 1], value, vallen);
ff152e3f
UD
264# endif
265
de4a40d0 266 np = KNOWN_VALUE (new_value);
a1ffb40e 267 if (__glibc_likely (np == NULL))
de4a40d0 268#endif
9e0f4072 269 {
c63bfa79 270#ifdef USE_TSEARCH
9401024e 271 np = new_value;
c63bfa79 272#endif
de4a40d0
UD
273 /* And remember the value. */
274 STORE_VALUE (np);
275 }
d5b1c5ed
EB
276#ifdef USE_TSEARCH
277 else
9401024e 278 free (new_value);
d5b1c5ed 279#endif
28f540f4 280 }
cb37d842 281
7a61e7f5
FW
282 /* Use release MO so that loads are sufficient to observe the
283 pointer contents because the CPU carries the dependency for
284 us. This also acts as a thread fence, making getenv
285 async-signal-safe. */
286 atomic_store_release (ep, np);
287 atomic_store_release (&__environ, result_environ);
28f540f4
RM
288 }
289
9e0f4072
RM
290 UNLOCK;
291
28f540f4
RM
292 return 0;
293}
196980f5 294
de4a40d0 295int
9d46370c 296setenv (const char *name, const char *value, int replace)
de4a40d0 297{
e17f8b61
RM
298 if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
299 {
300 __set_errno (EINVAL);
301 return -1;
302 }
303
de4a40d0
UD
304 return __add_to_environ (name, value, NULL, replace);
305}
306
61f9d0a3 307int
9d46370c 308unsetenv (const char *name)
196980f5 309{
7c5bb945 310 size_t len;
196980f5
RM
311 char **ep;
312
61f9d0a3
UD
313 if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
314 {
315 __set_errno (EINVAL);
316 return -1;
317 }
318
319 len = strlen (name);
320
9e0f4072
RM
321 LOCK;
322
7a61e7f5 323 ep = atomic_load_relaxed (&__environ);
1fa7ae05 324 if (ep != NULL)
7a61e7f5 325 while (true)
df1cf487 326 {
7a61e7f5
FW
327 char *entry = atomic_load_relaxed (ep);
328 if (entry == NULL)
329 break;
330 if (strncmp (entry, name, len) == 0 && entry[len] == '=')
df1cf487
YR
331 {
332 /* Found it. Remove this pointer by moving later ones back. */
333 char **dp = ep;
334
7a61e7f5
FW
335 while (true)
336 {
337 char *next_value = atomic_load_relaxed (dp + 1);
338 /* This store overwrites a value that has been
339 removed, or that has already been written to a
340 previous value. Release MO so that this store does
341 not get reordered before the counter update in the
342 previous loop iteration. */
343 atomic_store_release (dp, next_value);
344 /* Release store synchronizes with acquire loads in
345 getenv. Non-atomic update because there is just
346 one writer due to the lock.
347
348 See discussion of the counter check in getenv for
349 an explanation why this is sufficient synchronization. */
350 atomic_store_release (&__environ_counter,
351 atomic_load_relaxed (&__environ_counter)
352 + 1);
353 if (next_value == NULL)
354 break;
355 ++dp;
356 }
df1cf487
YR
357 /* Continue the loop in case NAME appears again. */
358 }
359 else
360 ++ep;
361 }
9e0f4072
RM
362
363 UNLOCK;
61f9d0a3
UD
364
365 return 0;
196980f5 366}
f0e44959
UD
367
368/* The `clearenv' was planned to be added to POSIX.1 but probably
369 never made it. Nevertheless the POSIX.9 standard (POSIX bindings
370 for Fortran 77) requires this function. */
371int
60d2f8f3 372clearenv (void)
f0e44959
UD
373{
374 LOCK;
7a61e7f5
FW
375 char **start_environ = atomic_load_relaxed (&__environ);
376 if (__environ_is_from_array_list (start_environ))
f0e44959 377 {
7a61e7f5
FW
378 /* Store null pointers to avoid strange effects when the array
379 is reused in setenv. */
380 for (char **ep = start_environ; *ep != NULL; ++ep)
381 atomic_store_relaxed (ep, NULL);
382 /* Update the counter similar to unsetenv, so that the writes in
383 setenv do not need to update the counter. */
384 atomic_store_release (&__environ_counter,
385 atomic_load_relaxed (&__environ_counter) + 1);
f0e44959
UD
386 }
387
7a61e7f5 388 atomic_store_relaxed (&__environ, NULL);
f0e44959
UD
389 UNLOCK;
390
391 return 0;
392}
3e5f5557 393#ifdef _LIBC
88677348
AZN
394void
395__libc_setenv_freemem (void)
69071b2a
UD
396{
397 /* Remove all traces. */
398 clearenv ();
399
7a61e7f5
FW
400 /* Clear all backing arrays. */
401 while (__environ_array_list != NULL)
402 {
12b4a1fc 403 free (__environ_array_list->array);
7a61e7f5
FW
404 void *ptr = __environ_array_list;
405 __environ_array_list = __environ_array_list->next;
406 free (ptr);
407 }
408
69071b2a
UD
409 /* Now remove the search tree. */
410 __tdestroy (known_values, free);
411 known_values = NULL;
412}
69071b2a 413
ff152e3f
UD
414# undef setenv
415# undef unsetenv
3e5f5557 416# undef clearenv
ff152e3f
UD
417weak_alias (__setenv, setenv)
418weak_alias (__unsetenv, unsetenv)
3e5f5557
UD
419weak_alias (__clearenv, clearenv)
420#endif