]>
git.ipfire.org Git - thirdparty/glibc.git/blob - stdlib/setenv.c
0ef5dde37348a98f58c8f5f148fd913e4842919c
1 /* Copyright (C) 1992-2025 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
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.
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
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
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. */
30 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
34 # if !defined errno && !defined HAVE_ERRNO_DECL
37 # define __set_errno(ev) ((errno) = (ev))
40 #if _LIBC || HAVE_STDLIB_H
43 #if _LIBC || HAVE_STRING_H
46 #if _LIBC || HAVE_UNISTD_H
51 # define __environ environ
52 # ifndef HAVE_ENVIRON_DECL
53 extern char **environ
;
58 /* This lock protects against simultaneous modifications of `environ'. */
59 # include <libc-lock.h>
60 __libc_lock_define_initialized (static, envlock
)
61 # define LOCK __libc_lock_lock (envlock)
62 # define UNLOCK __libc_lock_unlock (envlock)
68 /* In the GNU C library we must keep the namespace clean. */
70 # define setenv __setenv
71 # define unsetenv __unsetenv
72 # define clearenv __clearenv
73 # define tfind __tfind
74 # define tsearch __tsearch
77 /* In the GNU C library implementation we try to be more clever and
78 allow arbitrarily many changes of the environment given that the used
79 values are from a small set. Outside glibc this will eat up all
80 memory after a while. */
81 #if defined _LIBC || (defined HAVE_SEARCH_H && defined HAVE_TSEARCH \
83 # define USE_TSEARCH 1
86 /* This is a pointer to the root of the search tree with the known
88 static void *known_values
;
90 # define KNOWN_VALUE(Str) \
92 void *value = tfind (Str, &known_values, (__compar_fn_t) strcmp); \
93 value != NULL ? *(char **) value : NULL; \
95 # define STORE_VALUE(Str) \
96 tsearch (Str, &known_values, (__compar_fn_t) strcmp)
101 # define KNOWN_VALUE(Str) NULL
102 # define STORE_VALUE(Str) do { } while (0)
106 /* Allocate a new environment array and put it o the
107 __environ_array_list. Returns NULL on memory allocation
109 static struct environ_array
*
110 __environ_new_array (size_t required_size
)
112 /* No backing array yet, or insufficient room. */
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;
119 new_size
= __environ_array_list
->allocated
* 2;
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
)
127 struct environ_array
*target_array
= malloc (sizeof (*target_array
));
128 if (target_array
== NULL
)
134 target_array
->allocated
= new_size
;
135 target_array
->array
= new_array
;
136 assert (new_size
>= target_array
->allocated
);
138 /* Put it onto the list. */
139 target_array
->next
= __environ_array_list
;
140 __environ_array_list
= target_array
;
145 __add_to_environ (const char *name
, const char *value
, const char *combined
,
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. */
154 const size_t namelen
= strlen (name
);
156 if (combined
== NULL
)
157 vallen
= strlen (value
) + 1;
161 /* We have to get the pointer now that we have the lock and not earlier
162 since another thread might have created a new environment. */
163 char **start_environ
= atomic_load_relaxed (&__environ
);
164 char **ep
= start_environ
;
166 /* This gets written to __environ in the end. */
167 char **result_environ
= start_environ
;
169 /* Size of the environment if *ep == NULL. */
171 for (; *ep
!= NULL
; ++ep
)
172 if (strncmp (*ep
, name
, namelen
) == 0 && (*ep
)[namelen
] == '=')
175 if (ep
== NULL
|| __glibc_likely (*ep
== NULL
))
177 /* The scanning loop above reached the end of the environment.
178 Add a new string to it. */
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. */
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. */
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
;
202 /* Allocate a new array. */
203 target_array
= __environ_new_array (required_size
);
204 if (target_array
== NULL
)
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:
216 environ = &environ[1];
218 The forward copy avoids clobbering values that still
219 needing copying. This code handles the case
220 start_environ == ep == NULL, too. */
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
226 target_array
->array
[i
] = atomic_load_relaxed (start_environ
+ i
);
228 /* This is the new place where we should add the element. */
229 ep
= target_array
->array
+ i
;
231 /* Add the null terminator in case there was a pointer there
235 /* And __environ should be repointed to our array. */
236 result_environ
= target_array
->array
;
240 if (replace
|| *ep
== NULL
)
244 /* Use the user string if given. */
245 if (combined
!= NULL
)
246 np
= (char *) combined
;
249 const size_t varlen
= namelen
+ 1 + vallen
;
251 char *new_value
= malloc (varlen
);
252 if (new_value
== NULL
)
258 __mempcpy (__mempcpy (__mempcpy (new_value
, name
, namelen
), "=", 1),
261 memcpy (new_value
, name
, namelen
);
262 new_value
[namelen
] = '=';
263 memcpy (&new_value
[namelen
+ 1], value
, vallen
);
266 np
= KNOWN_VALUE (new_value
);
267 if (__glibc_likely (np
== NULL
))
273 /* And remember the value. */
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
);
296 setenv (const char *name
, const char *value
, int replace
)
298 if (name
== NULL
|| *name
== '\0' || strchr (name
, '=') != NULL
)
300 __set_errno (EINVAL
);
304 return __add_to_environ (name
, value
, NULL
, replace
);
308 unsetenv (const char *name
)
313 if (name
== NULL
|| *name
== '\0' || strchr (name
, '=') != NULL
)
315 __set_errno (EINVAL
);
323 ep
= atomic_load_relaxed (&__environ
);
327 char *entry
= atomic_load_relaxed (ep
);
330 if (strncmp (entry
, name
, len
) == 0 && entry
[len
] == '=')
332 /* Found it. Remove this pointer by moving later ones back. */
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.
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
)
353 if (next_value
== NULL
)
357 /* Continue the loop in case NAME appears again. */
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. */
375 char **start_environ
= atomic_load_relaxed (&__environ
);
376 if (__environ_is_from_array_list (start_environ
))
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);
388 atomic_store_relaxed (&__environ
, NULL
);
395 __libc_setenv_freemem (void)
397 /* Remove all traces. */
400 /* Clear all backing arrays. */
401 while (__environ_array_list
!= NULL
)
403 free (__environ_array_list
->array
);
404 void *ptr
= __environ_array_list
;
405 __environ_array_list
= __environ_array_list
->next
;
409 /* Now remove the search tree. */
410 __tdestroy (known_values
, free
);
417 weak_alias (__setenv
, setenv
)
418 weak_alias (__unsetenv
, unsetenv
)
419 weak_alias (__clearenv
, clearenv
)