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. */
23 # pragma GCC visibility push(hidden)
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>
42 tunables_strdup (const char *in
)
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. */
53 _dl_fatal_printf ("failed to allocate memory to process tunables\n");
62 get_next_env (char **envp
, char **name
, size_t *namelen
, char **val
,
65 while (envp
!= NULL
&& *envp
!= NULL
)
68 char *envline
= *envp
++;
71 while (envline
[len
] != '\0' && envline
[len
] != '=')
74 /* Just the name and no value, go to the next one. */
75 if (envline
[len
] == '\0')
80 *val
= &envline
[len
+ 1];
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;
103 bool unsigned_cmp
= unsigned_tunable_type (cur
->type
.type_code
);
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
))
113 if (tunable_val_gt (max
, cur
->type
.max
, unsigned_cmp
))
116 /* Skip both bounds if they're inconsistent. */
117 if (tunable_val_gt (min
, max
, unsigned_cmp
))
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
))
128 cur
->val
.numval
= val
;
131 cur
->initialized
= true;
134 /* Validate range of the input value and initialize the tunable CUR if it looks
137 tunable_initialize (tunable_t
*cur
, const char *strval
)
141 if (cur
->type
.type_code
!= TUNABLE_TYPE_STRING
)
142 val
.numval
= (tunable_num_t
) _dl_strtoul (strval
, NULL
);
145 do_tunable_update_val (cur
, &val
, NULL
, NULL
);
149 __tunable_set_val (tunable_id_t id
, tunable_val_t
*valp
, tunable_num_t
*minp
,
152 tunable_t
*cur
= &tunable_list
[id
];
154 do_tunable_update_val (cur
, valp
, minp
, maxp
);
157 /* Parse the tunable string VALSTRING. VALSTRING is a duplicated value,
158 where delimiters ':' are replaced with '\0', so string tunables are null
161 parse_tunables (char *valstring
)
163 if (valstring
== NULL
|| *valstring
== '\0')
173 /* First, find where the name ends. */
174 while (*p
!= '=' && *p
!= ':' && *p
!= '\0')
177 /* If we reach the end of the string before getting a valid name-value
182 /* We did not find a valid name-value pair before encountering the
193 const char *value
= p
;
195 while (*p
!= '=' && *p
!= ':' && *p
!= '\0')
205 /* Add the tunable if it exists. */
206 for (size_t i
= 0; i
< sizeof (tunable_list
) / sizeof (tunable_t
); i
++)
208 tunable_t
*cur
= &tunable_list
[i
];
210 if (tunable_is_name (cur
->name
, name
))
212 tunable_initialize (cur
, value
);
219 /* Initialize the tunables list from the environment. For now we only use the
220 ENV_ALIAS to find values. Later we will also use the tunable names to find
223 __tunables_init (char **envp
)
225 char *envname
= NULL
;
228 char **prev_envp
= envp
;
230 /* Ignore tunables for AT_SECURE programs. */
231 if (__libc_enable_secure
)
234 while ((envp
= get_next_env (envp
, &envname
, &len
, &envval
,
235 &prev_envp
)) != NULL
)
237 if (tunable_is_name ("GLIBC_TUNABLES", envname
))
239 parse_tunables (tunables_strdup (envval
));
243 for (int i
= 0; i
< sizeof (tunable_list
) / sizeof (tunable_t
); i
++)
245 tunable_t
*cur
= &tunable_list
[i
];
247 /* Skip over tunables that have either been set already or should be
249 if (cur
->initialized
|| cur
->env_alias
[0] == '\0')
252 const char *name
= cur
->env_alias
;
254 /* We have a match. Initialize and move on to the next line. */
255 if (tunable_is_name (name
, envname
))
257 tunable_initialize (cur
, envval
);
265 __tunables_print (void)
267 for (int i
= 0; i
< array_length (tunable_list
); i
++)
269 const tunable_t
*cur
= &tunable_list
[i
];
270 if (cur
->type
.type_code
== TUNABLE_TYPE_STRING
271 && cur
->val
.strval
== NULL
)
272 _dl_printf ("%s:\n", cur
->name
);
275 _dl_printf ("%s: ", cur
->name
);
276 switch (cur
->type
.type_code
)
278 case TUNABLE_TYPE_INT_32
:
279 _dl_printf ("%d (min: %d, max: %d)\n",
280 (int) cur
->val
.numval
,
282 (int) cur
->type
.max
);
284 case TUNABLE_TYPE_UINT_64
:
285 _dl_printf ("0x%lx (min: 0x%lx, max: 0x%lx)\n",
286 (long int) cur
->val
.numval
,
287 (long int) cur
->type
.min
,
288 (long int) cur
->type
.max
);
290 case TUNABLE_TYPE_SIZE_T
:
291 _dl_printf ("0x%zx (min: 0x%zx, max: 0x%zx)\n",
292 (size_t) cur
->val
.numval
,
293 (size_t) cur
->type
.min
,
294 (size_t) cur
->type
.max
);
296 case TUNABLE_TYPE_STRING
:
297 _dl_printf ("%s\n", cur
->val
.strval
);
300 __builtin_unreachable ();
306 /* Set the tunable value. This is called by the module that the tunable exists
309 __tunable_get_val (tunable_id_t id
, void *valp
, tunable_callback_t callback
)
311 tunable_t
*cur
= &tunable_list
[id
];
313 switch (cur
->type
.type_code
)
315 case TUNABLE_TYPE_UINT_64
:
317 *((uint64_t *) valp
) = (uint64_t) cur
->val
.numval
;
320 case TUNABLE_TYPE_INT_32
:
322 *((int32_t *) valp
) = (int32_t) cur
->val
.numval
;
325 case TUNABLE_TYPE_SIZE_T
:
327 *((size_t *) valp
) = (size_t) cur
->val
.numval
;
330 case TUNABLE_TYPE_STRING
:
332 *((const char **)valp
) = cur
->val
.strval
;
336 __builtin_unreachable ();
339 if (cur
->initialized
&& callback
!= NULL
)
340 callback (&cur
->val
);
343 rtld_hidden_def (__tunable_get_val
)