elf: Refactor process_envvars
[glibc.git] / elf / dl-tunables.c
blob644d21d1b0427d8726f38570590eef0918eb8be6
1 /* The tunable framework. See the README.tunables to know how to use the
2 tunable in a glibc module.
4 Copyright (C) 2016-2023 Free Software Foundation, Inc.
5 This file is part of the GNU C Library.
7 The GNU C Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
12 The GNU C Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with the GNU C Library; if not, see
19 <https://www.gnu.org/licenses/>. */
21 /* Mark symbols hidden in static PIE for early self relocation to work. */
22 #if BUILD_PIE_DEFAULT
23 # pragma GCC visibility push(hidden)
24 #endif
25 #include <startup.h>
26 #include <stdint.h>
27 #include <stdbool.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <sysdep.h>
31 #include <fcntl.h>
32 #include <ldsodefs.h>
33 #include <array_length.h>
34 #include <dl-minimal-malloc.h>
36 #define TUNABLES_INTERNAL 1
37 #include "dl-tunables.h"
39 #include <not-errno.h>
41 static char *
42 tunables_strdup (const char *in)
44 size_t i = 0;
46 while (in[i++] != '\0');
47 char *out = __minimal_malloc (i + 1);
49 /* For most of the tunables code, we ignore user errors. However,
50 this is a system error - and running out of memory at program
51 startup should be reported, so we do. */
52 if (out == NULL)
53 _dl_fatal_printf ("failed to allocate memory to process tunables\n");
55 while (i-- > 0)
56 out[i] = in[i];
58 return out;
61 static char **
62 get_next_env (char **envp, char **name, size_t *namelen, char **val,
63 char ***prev_envp)
65 while (envp != NULL && *envp != NULL)
67 char **prev = envp;
68 char *envline = *envp++;
69 int len = 0;
71 while (envline[len] != '\0' && envline[len] != '=')
72 len++;
74 /* Just the name and no value, go to the next one. */
75 if (envline[len] == '\0')
76 continue;
78 *name = envline;
79 *namelen = len;
80 *val = &envline[len + 1];
81 *prev_envp = prev;
83 return envp;
86 return NULL;
89 static void
90 do_tunable_update_val (tunable_t *cur, const tunable_val_t *valp,
91 const tunable_num_t *minp,
92 const tunable_num_t *maxp)
94 tunable_num_t val, min, max;
96 if (cur->type.type_code == TUNABLE_TYPE_STRING)
98 cur->val.strval = valp->strval;
99 cur->initialized = true;
100 return;
103 bool unsigned_cmp = unsigned_tunable_type (cur->type.type_code);
105 val = valp->numval;
106 min = minp != NULL ? *minp : cur->type.min;
107 max = maxp != NULL ? *maxp : cur->type.max;
109 /* We allow only increasingly restrictive bounds. */
110 if (tunable_val_lt (min, cur->type.min, unsigned_cmp))
111 min = cur->type.min;
113 if (tunable_val_gt (max, cur->type.max, unsigned_cmp))
114 max = cur->type.max;
116 /* Skip both bounds if they're inconsistent. */
117 if (tunable_val_gt (min, max, unsigned_cmp))
119 min = cur->type.min;
120 max = cur->type.max;
123 /* Bail out if the bounds are not valid. */
124 if (tunable_val_lt (val, min, unsigned_cmp)
125 || tunable_val_lt (max, val, unsigned_cmp))
126 return;
128 cur->val.numval = val;
129 cur->type.min = min;
130 cur->type.max = max;
131 cur->initialized = true;
134 /* Validate range of the input value and initialize the tunable CUR if it looks
135 good. */
136 static void
137 tunable_initialize (tunable_t *cur, const char *strval)
139 tunable_val_t val;
141 if (cur->type.type_code != TUNABLE_TYPE_STRING)
142 val.numval = (tunable_num_t) _dl_strtoul (strval, NULL);
143 else
144 val.strval = strval;
145 do_tunable_update_val (cur, &val, NULL, NULL);
148 bool
149 __tunable_is_initialized (tunable_id_t id)
151 return tunable_list[id].initialized;
153 rtld_hidden_def (__tunable_is_initialized)
155 void
156 __tunable_set_val (tunable_id_t id, tunable_val_t *valp, tunable_num_t *minp,
157 tunable_num_t *maxp)
159 tunable_t *cur = &tunable_list[id];
161 do_tunable_update_val (cur, valp, minp, maxp);
164 struct tunable_toset_t
166 tunable_t *t;
167 const char *value;
170 enum { tunables_list_size = array_length (tunable_list) };
172 /* Parse the tunable string VALSTRING and set TUNABLES with the found tunables
173 and their respective strings. VALSTRING is a duplicated values, where
174 delimiters ':' are replaced with '\0', so string tunables are null
175 terminated.
176 Return the number of tunables found (including 0 if the string is empty)
177 or -1 if for an ill-formatted definition. */
178 static int
179 parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
181 if (valstring == NULL || *valstring == '\0')
182 return 0;
184 char *p = valstring;
185 bool done = false;
186 int ntunables = 0;
188 while (!done)
190 char *name = p;
192 /* First, find where the name ends. */
193 while (*p != '=' && *p != ':' && *p != '\0')
194 p++;
196 /* If we reach the end of the string before getting a valid name-value
197 pair, bail out. */
198 if (*p == '\0')
199 return -1;
201 /* We did not find a valid name-value pair before encountering the
202 colon. */
203 if (*p == ':')
205 p++;
206 continue;
209 /* Skip the '='. */
210 p++;
212 char *value = p;
214 while (*p != '=' && *p != ':' && *p != '\0')
215 p++;
217 if (*p == '=')
218 return -1;
219 else if (*p == '\0')
220 done = true;
221 else
222 *p++ = '\0';
224 /* Add the tunable if it exists. */
225 for (size_t i = 0; i < tunables_list_size; i++)
227 tunable_t *cur = &tunable_list[i];
229 if (tunable_is_name (cur->name, name))
231 tunables[ntunables++] = (struct tunable_toset_t) { cur, value };
232 break;
237 return ntunables;
240 static void
241 parse_tunables (char *valstring)
243 struct tunable_toset_t tunables[tunables_list_size];
244 int ntunables = parse_tunables_string (valstring, tunables);
245 if (ntunables == -1)
247 _dl_error_printf (
248 "WARNING: ld.so: invalid GLIBC_TUNABLES `%s': ignored.\n", valstring);
249 return;
252 for (int i = 0; i < ntunables; i++)
253 tunable_initialize (tunables[i].t, tunables[i].value);
256 /* Initialize the tunables list from the environment. For now we only use the
257 ENV_ALIAS to find values. Later we will also use the tunable names to find
258 values. */
259 void
260 __tunables_init (char **envp)
262 char *envname = NULL;
263 char *envval = NULL;
264 size_t len = 0;
265 char **prev_envp = envp;
267 /* Ignore tunables for AT_SECURE programs. */
268 if (__libc_enable_secure)
269 return;
271 while ((envp = get_next_env (envp, &envname, &len, &envval,
272 &prev_envp)) != NULL)
274 if (tunable_is_name ("GLIBC_TUNABLES", envname))
276 parse_tunables (tunables_strdup (envval));
277 continue;
280 for (int i = 0; i < tunables_list_size; i++)
282 tunable_t *cur = &tunable_list[i];
284 /* Skip over tunables that have either been set already or should be
285 skipped. */
286 if (cur->initialized || cur->env_alias[0] == '\0')
287 continue;
289 const char *name = cur->env_alias;
291 /* We have a match. Initialize and move on to the next line. */
292 if (tunable_is_name (name, envname))
294 tunable_initialize (cur, envval);
295 break;
301 void
302 __tunables_print (void)
304 for (int i = 0; i < array_length (tunable_list); i++)
306 const tunable_t *cur = &tunable_list[i];
307 if (cur->type.type_code == TUNABLE_TYPE_STRING
308 && cur->val.strval == NULL)
309 _dl_printf ("%s:\n", cur->name);
310 else
312 _dl_printf ("%s: ", cur->name);
313 switch (cur->type.type_code)
315 case TUNABLE_TYPE_INT_32:
316 _dl_printf ("%d (min: %d, max: %d)\n",
317 (int) cur->val.numval,
318 (int) cur->type.min,
319 (int) cur->type.max);
320 break;
321 case TUNABLE_TYPE_UINT_64:
322 _dl_printf ("0x%lx (min: 0x%lx, max: 0x%lx)\n",
323 (long int) cur->val.numval,
324 (long int) cur->type.min,
325 (long int) cur->type.max);
326 break;
327 case TUNABLE_TYPE_SIZE_T:
328 _dl_printf ("0x%zx (min: 0x%zx, max: 0x%zx)\n",
329 (size_t) cur->val.numval,
330 (size_t) cur->type.min,
331 (size_t) cur->type.max);
332 break;
333 case TUNABLE_TYPE_STRING:
334 _dl_printf ("%s\n", cur->val.strval);
335 break;
336 default:
337 __builtin_unreachable ();
343 void
344 __tunable_get_default (tunable_id_t id, void *valp)
346 tunable_t *cur = &tunable_list[id];
348 switch (cur->type.type_code)
350 case TUNABLE_TYPE_UINT_64:
352 *((uint64_t *) valp) = (uint64_t) cur->def.numval;
353 break;
355 case TUNABLE_TYPE_INT_32:
357 *((int32_t *) valp) = (int32_t) cur->def.numval;
358 break;
360 case TUNABLE_TYPE_SIZE_T:
362 *((size_t *) valp) = (size_t) cur->def.numval;
363 break;
365 case TUNABLE_TYPE_STRING:
367 *((const char **)valp) = cur->def.strval;
368 break;
370 default:
371 __builtin_unreachable ();
374 rtld_hidden_def (__tunable_get_default)
376 /* Set the tunable value. This is called by the module that the tunable exists
377 in. */
378 void
379 __tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
381 tunable_t *cur = &tunable_list[id];
383 switch (cur->type.type_code)
385 case TUNABLE_TYPE_UINT_64:
387 *((uint64_t *) valp) = (uint64_t) cur->val.numval;
388 break;
390 case TUNABLE_TYPE_INT_32:
392 *((int32_t *) valp) = (int32_t) cur->val.numval;
393 break;
395 case TUNABLE_TYPE_SIZE_T:
397 *((size_t *) valp) = (size_t) cur->val.numval;
398 break;
400 case TUNABLE_TYPE_STRING:
402 *((const char **)valp) = cur->val.strval;
403 break;
405 default:
406 __builtin_unreachable ();
409 if (cur->initialized && callback != NULL)
410 callback (&cur->val);
413 rtld_hidden_def (__tunable_get_val)