[PATCH v2] Break out ldconfig parser
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Tue Mar 3 14:37:07 GMT 2026
On 26/02/26 23:49, DJ Delorie wrote:
>
> Florian Weimer <fweimer@redhat.com> writes:
>> I think we should start giving the ldconfig files ldconfig- or similar
>> prefixes, so that things appear a bit less crowded in the elf directory.
>
> Renamed.
>
>> Do we need a separate parseconf.h file, or could we stick it into an
>> existing shared header?
>
> I moved it to ldconfig.h, which for some reason is in sysdeps...
>
>> And I think the function comment should be a bit more elaborate.
>
> Documented. I also cleaned up the API a bit.
>
> elf: factor out ld.conf parsing
LGTM, thanks. The ldconfig_parse_config could be marked as hidden, but
the attribute_hidden expands to nothing for ldconfig build.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
>
> diff --git a/elf/Makefile b/elf/Makefile
> index 1a0ed49bc3..21ea58b10b 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -220,6 +220,7 @@ install-rootsbin += ldconfig
> ldconfig-modules := \
> cache \
> chroot_canon \
> + ldconfig-parse \
> readlib \
> static-stubs \
> stringtable \
> diff --git a/elf/ldconfig-parse.c b/elf/ldconfig-parse.c
> new file mode 100644
> index 0000000000..9da5bd96e6
> --- /dev/null
> +++ b/elf/ldconfig-parse.c
> @@ -0,0 +1,204 @@
> +/* Copyright (C) 2026 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + This program is free software; you can redistribute it and/or modify
> + it under the terms of the GNU General Public License as published
> + by the Free Software Foundation; version 2 of the License, or
> + (at your option) any later version.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program; if not, see <https://www.gnu.org/licenses/>. */
> +
> +#include <stdbool.h>
> +#include <glob.h>
> +#include <stdio.h>
> +#include <error.h>
> +#include <errno.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <locale.h>
> +#include <libintl.h>
> +#include <ctype.h>
> +#include <stdio_ext.h>
> +#include <libgen.h>
> +
> +#include <ldconfig.h>
> +
> +static void
> +parse_conf_include (const char *config_file, unsigned int lineno,
> + bool do_chroot, const char *pattern,
> + char *opt_chroot, ldconfig_parse_config_cb *cb);
> +
> +static void
> +ldconfig_parse_config_1 (const char *filename, bool do_chroot,
> + char *opt_chroot,
> + ldconfig_parse_config_cb *callback);
> +
> +/* Parse a configuration file.
> +
> + Parameters:
> +
> + filename - The name of the file. It may be a full path or relative.
> +
> + opt_chroot - If non-NULL, all paths are relative to this.
> +
> + callback - for each non-blank line in the file, this function is called
> + with the line and it's location.
> + */
> +
> +void
> +ldconfig_parse_config (const char *filename, char *opt_chroot,
> + ldconfig_parse_config_cb *callback)
> +{
> + ldconfig_parse_config_1 (filename, true, opt_chroot, callback);
> +}
> +
> +static void
> +ldconfig_parse_config_1 (const char *filename, bool do_chroot,
> + char *opt_chroot, ldconfig_parse_config_cb *callback)
> +{
> + FILE *file = NULL;
> + char *line = NULL;
> + const char *canon;
> + size_t len = 0;
> + unsigned int lineno;
> +
> + if (do_chroot && opt_chroot)
> + {
> + canon = chroot_canon (opt_chroot, filename);
> + if (canon)
> + file = fopen (canon, "r");
> + else
> + canon = filename;
> + }
> + else
> + {
> + canon = filename;
> + file = fopen (filename, "r");
> + }
> +
> + if (file == NULL)
> + {
> + if (errno != ENOENT)
> + error (0, errno, _("\
> +Warning: ignoring configuration file that cannot be opened: %s"),
> + canon);
> + if (canon != filename)
> + free ((char *) canon);
> + return;
> + }
> +
> + /* No threads use this stream. */
> + __fsetlocking (file, FSETLOCKING_BYCALLER);
> +
> + if (canon != filename)
> + free ((char *) canon);
> +
> + lineno = 0;
> + do
> + {
> + ssize_t n = getline (&line, &len, file);
> + if (n < 0)
> + break;
> +
> + ++lineno;
> + if (line[n - 1] == '\n')
> + line[n - 1] = '\0';
> +
> + /* Because the file format does not know any form of quoting we
> + can search forward for the next '#' character and if found
> + make it terminating the line. */
> + *strchrnul (line, '#') = '\0';
> +
> + /* Remove leading whitespace. NUL is no whitespace character. */
> + char *cp = line;
> + while (isspace (*cp))
> + ++cp;
> +
> + /* If the line is blank it is ignored. */
> + if (cp[0] == '\0')
> + continue;
> +
> + if (!strncmp (cp, "include", 7) && isblank (cp[7]))
> + {
> + char *dir;
> + cp += 8;
> + while ((dir = strsep (&cp, " \t")) != NULL)
> + if (dir[0] != '\0')
> + parse_conf_include (filename, lineno, do_chroot, dir, opt_chroot, callback);
> + }
> + else
> + (*callback) (cp, filename, lineno);
> + }
> + while (!feof_unlocked (file));
> +
> + /* Free buffer and close file. */
> + free (line);
> + fclose (file);
> +}
> +
> +/* Handle one word in an `include' line, a glob pattern of additional
> + config files to read. */
> +void
> +parse_conf_include (const char *config_file, unsigned int lineno,
> + bool do_chroot, const char *pattern, char *opt_chroot,
> + ldconfig_parse_config_cb *callback)
> +{
> + if (opt_chroot != NULL && pattern[0] != '/')
> + error (EXIT_FAILURE, 0,
> + _("need absolute file name for configuration file when using -r"));
> +
> + char *copy = NULL;
> + if (pattern[0] != '/' && strchr (config_file, '/') != NULL)
> + {
> + if (asprintf (©, "%s/%s", dirname (strdupa (config_file)),
> + pattern) < 0)
> + error (EXIT_FAILURE, 0, _("memory exhausted"));
> + pattern = copy;
> + }
> +
> + glob64_t gl;
> + int result;
> + if (do_chroot && opt_chroot)
> + {
> + char *canon = chroot_canon (opt_chroot, pattern);
> + if (canon == NULL)
> + return;
> + result = glob64 (canon, 0, NULL, &gl);
> + free (canon);
> + }
> + else
> + result = glob64 (pattern, 0, NULL, &gl);
> +
> + switch (result)
> + {
> + case 0:
> + for (size_t i = 0; i < gl.gl_pathc; ++i)
> + ldconfig_parse_config_1 (gl.gl_pathv[i], false, opt_chroot, callback);
> + globfree64 (&gl);
> + break;
> +
> + case GLOB_NOMATCH:
> + break;
> +
> + case GLOB_NOSPACE:
> + errno = ENOMEM;
> + [[fallthrough]];
> + case GLOB_ABORTED:
> + if (opt_verbose)
> + error (0, errno, _("%s:%u: cannot read directory %s"),
> + config_file, lineno, pattern);
> + break;
> +
> + default:
> + abort ();
> + break;
> + }
> +
> + free (copy);
> +}
> diff --git a/elf/ldconfig.c b/elf/ldconfig.c
> index aca967403a..070e933df6 100644
> --- a/elf/ldconfig.c
> +++ b/elf/ldconfig.c
> @@ -420,6 +420,16 @@ add_dir_1 (const char *line, const char *from_file, int from_line)
> free (path);
> }
>
> +static void
> +add_dir_callback (const char *line, const char *from_file, int from_line)
> +{
> + if (!strncasecmp (line, "hwcap", 5) && isblank (line[5]))
> + error (0, 0, _("%s:%u: hwcap directive ignored"), from_file, from_line);
> + else
> + add_dir_1 (line, from_file, from_line);
> +}
> +
> +
> static void
> add_dir (const char *line)
> {
> @@ -1017,156 +1027,6 @@ search_dirs (void)
> }
>
>
> -static void parse_conf_include (const char *config_file, unsigned int lineno,
> - bool do_chroot, const char *pattern);
> -
> -/* Parse configuration file. */
> -static void
> -parse_conf (const char *filename, bool do_chroot)
> -{
> - FILE *file = NULL;
> - char *line = NULL;
> - const char *canon;
> - size_t len = 0;
> - unsigned int lineno;
> -
> - if (do_chroot && opt_chroot)
> - {
> - canon = chroot_canon (opt_chroot, filename);
> - if (canon)
> - file = fopen (canon, "r");
> - else
> - canon = filename;
> - }
> - else
> - {
> - canon = filename;
> - file = fopen (filename, "r");
> - }
> -
> - if (file == NULL)
> - {
> - if (errno != ENOENT)
> - error (0, errno, _("\
> -Warning: ignoring configuration file that cannot be opened: %s"),
> - canon);
> - if (canon != filename)
> - free ((char *) canon);
> - return;
> - }
> -
> - /* No threads use this stream. */
> - __fsetlocking (file, FSETLOCKING_BYCALLER);
> -
> - if (canon != filename)
> - free ((char *) canon);
> -
> - lineno = 0;
> - do
> - {
> - ssize_t n = getline (&line, &len, file);
> - if (n < 0)
> - break;
> -
> - ++lineno;
> - if (line[n - 1] == '\n')
> - line[n - 1] = '\0';
> -
> - /* Because the file format does not know any form of quoting we
> - can search forward for the next '#' character and if found
> - make it terminating the line. */
> - *strchrnul (line, '#') = '\0';
> -
> - /* Remove leading whitespace. NUL is no whitespace character. */
> - char *cp = line;
> - while (isspace (*cp))
> - ++cp;
> -
> - /* If the line is blank it is ignored. */
> - if (cp[0] == '\0')
> - continue;
> -
> - if (!strncmp (cp, "include", 7) && isblank (cp[7]))
> - {
> - char *dir;
> - cp += 8;
> - while ((dir = strsep (&cp, " \t")) != NULL)
> - if (dir[0] != '\0')
> - parse_conf_include (filename, lineno, do_chroot, dir);
> - }
> - else if (!strncasecmp (cp, "hwcap", 5) && isblank (cp[5]))
> - error (0, 0, _("%s:%u: hwcap directive ignored"), filename, lineno);
> - else
> - add_dir_1 (cp, filename, lineno);
> - }
> - while (!feof_unlocked (file));
> -
> - /* Free buffer and close file. */
> - free (line);
> - fclose (file);
> -}
> -
> -/* Handle one word in an `include' line, a glob pattern of additional
> - config files to read. */
> -static void
> -parse_conf_include (const char *config_file, unsigned int lineno,
> - bool do_chroot, const char *pattern)
> -{
> - if (opt_chroot != NULL && pattern[0] != '/')
> - error (EXIT_FAILURE, 0,
> - _("need absolute file name for configuration file when using -r"));
> -
> - char *copy = NULL;
> - if (pattern[0] != '/' && strchr (config_file, '/') != NULL)
> - {
> - if (asprintf (©, "%s/%s", dirname (strdupa (config_file)),
> - pattern) < 0)
> - error (EXIT_FAILURE, 0, _("memory exhausted"));
> - pattern = copy;
> - }
> -
> - glob64_t gl;
> - int result;
> - if (do_chroot && opt_chroot)
> - {
> - char *canon = chroot_canon (opt_chroot, pattern);
> - if (canon == NULL)
> - return;
> - result = glob64 (canon, 0, NULL, &gl);
> - free (canon);
> - }
> - else
> - result = glob64 (pattern, 0, NULL, &gl);
> -
> - switch (result)
> - {
> - case 0:
> - for (size_t i = 0; i < gl.gl_pathc; ++i)
> - parse_conf (gl.gl_pathv[i], false);
> - globfree64 (&gl);
> - break;
> -
> - case GLOB_NOMATCH:
> - break;
> -
> - case GLOB_NOSPACE:
> - errno = ENOMEM;
> - [[fallthrough]];
> - case GLOB_ABORTED:
> - if (opt_verbose)
> - error (0, errno, _("%s:%u: cannot read directory %s"),
> - config_file, lineno, pattern);
> - break;
> -
> - default:
> - abort ();
> - break;
> - }
> -
> - free (copy);
> -}
> -
> -
> int
> main (int argc, char **argv)
> {
> @@ -1285,7 +1145,7 @@ main (int argc, char **argv)
>
> if (!opt_only_cline)
> {
> - parse_conf (config_file, true);
> + ldconfig_parse_config (config_file, opt_chroot, add_dir_callback);
>
> /* Always add the standard search paths. */
> add_system_dir (SLIBDIR);
> diff --git a/sysdeps/generic/ldconfig.h b/sysdeps/generic/ldconfig.h
> index 5913bf40fc..5a2a0f1d8d 100644
> --- a/sysdeps/generic/ldconfig.h
> +++ b/sysdeps/generic/ldconfig.h
> @@ -111,6 +111,13 @@ enum opt_format
>
> extern enum opt_format opt_format;
>
> +/* Declared in ldconfig-parse.c */
> +typedef void (ldconfig_parse_config_cb) (const char *line,
> + const char *from_file, int from_line);
> +
> +void ldconfig_parse_config (const char *filename, char *opt_chroot,
> + ldconfig_parse_config_cb *cb);
> +
> /* Prototypes for a few program-wide used functions. */
> #include <programs/xmalloc.h>
>
>
More information about the Libc-alpha
mailing list