aboutsummaryrefslogtreecommitdiff
path: root/elf/ldconfig.c
diff options
context:
space:
mode:
Diffstat (limited to 'elf/ldconfig.c')
-rw-r--r--elf/ldconfig.c647
1 files changed, 647 insertions, 0 deletions
diff --git a/elf/ldconfig.c b/elf/ldconfig.c
new file mode 100644
index 0000000000..6c2bcb2d4f
--- /dev/null
+++ b/elf/ldconfig.c
@@ -0,0 +1,647 @@
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Andreas Jaeger <aj@suse.de>, 1999.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <argp.h>
+#include <dirent.h>
+#include <error.h>
+#include <errno.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "ldconfig.h"
+
+#ifndef LD_SO_CACHE
+# define LD_SO_CACHE "/etc/ld.so.cache"
+#endif
+
+#ifndef LD_SO_CONF
+# define LD_SO_CONF "/etc/ld.so.conf"
+#endif
+
+/* Get libc version number. */
+#include <version.h>
+
+#define PACKAGE _libc_intl_domainname
+
+struct lib_entry
+ {
+ int flags;
+ char *lib;
+ char *path;
+ };
+
+static const struct
+{
+ const char *name;
+ int flag;
+} lib_types [] =
+{
+ {"libc4", FLAG_LIBC4},
+ {"libc5", FLAG_ELF_LIBC5},
+ {"libc6", FLAG_ELF_LIBC6},
+ {"glibc2", FLAG_ELF_LIBC6}
+};
+
+
+/* List of directories to handle. */
+struct dir_entry
+{
+ char *path;
+ int flag;
+ struct dir_entry *next;
+};
+
+/* The list is unsorted, contains no duplicates. Entries are added at
+ the end. */
+static struct dir_entry *dir_entries;
+
+/* Flags for different options. */
+/* Print Cache. */
+static int opt_print_cache = 0;
+
+/* Be verbose. */
+int opt_verbose = 0;
+
+/* Build cache. */
+static int opt_build_cache = 1;
+
+/* Generate links. */
+static int opt_link = 1;
+
+/* Only process directories specified on the command line. */
+static int opt_only_cline = 0;
+
+/* Path to root for chroot. */
+static char *opt_chroot;
+
+/* Cache file to use. */
+static const char *cache_file;
+
+/* Configuration file. */
+static const char *config_file;
+
+/* Name and version of program. */
+static void print_version (FILE *stream, struct argp_state *state);
+void (*argp_program_version_hook) (FILE *, struct argp_state *)
+ = print_version;
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { "print-cache", 'p', NULL, 0, N_("Print cache"), 0},
+ { "verbose", 'v', NULL, 0, N_("Generate verbose messages"), 0},
+ { NULL, 'N', NULL, 0, N_("Don't build cache"), 0},
+ { NULL, 'X', NULL, 0, N_("Don't generate links"), 0},
+ { NULL, 'r', "ROOT", 0, N_("Change to and use ROOT as root directory"), 0},
+ { NULL, 'C', "CACHE", 0, N_("Use CACHE as cache file"), 0},
+ { NULL, 'f', "CONF", 0, N_("Use CONF as configuration file"), 0},
+ { NULL, 'n', NULL, 0, N_("Only process directories specified on the command line. Don't build cache."), 0},
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+/* Short description of program. */
+static const char doc[] = N_("Configure Dynamic Linker Run Time Bindings.");
+
+/* Prototype for option handler. */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, NULL, doc, NULL, NULL, NULL
+};
+
+
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case 'C':
+ cache_file = arg;
+ break;
+ case 'f':
+ config_file = arg;
+ break;
+ case 'N':
+ opt_build_cache = 0;
+ break;
+ case 'n':
+ opt_build_cache = 0;
+ opt_only_cline = 1;
+ break;
+ case 'p':
+ opt_print_cache = 1;
+ break;
+ case 'r':
+ opt_chroot = arg;
+ break;
+ case 'v':
+ opt_verbose = 1;
+ break;
+ case 'X':
+ opt_link = 0;
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+/* Print the version information. */
+static void
+print_version (FILE *stream, struct argp_state *state)
+{
+ fprintf (stream, "ldconfig (GNU %s) %s\n", PACKAGE, VERSION);
+ fprintf (stream, gettext ("\
+Copyright (C) %s Free Software Foundation, Inc.\n\
+This is free software; see the source for copying conditions. There is NO\n\
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
+"), "1999");
+ fprintf (stream, gettext ("Written by %s.\n"),
+ "Andreas Jaeger");
+}
+
+/* Add one directory to the list of directories to process. */
+static void
+add_dir (const char *line)
+{
+ char *equal_sign;
+ struct dir_entry *entry, *ptr, *prev;
+ unsigned int i;
+
+ entry = xmalloc (sizeof (struct dir_entry));
+ entry->next = NULL;
+
+ /* Search for an '=' sign. */
+ entry->path = xstrdup (line);
+ equal_sign = strchr (entry->path, '=');
+ if (equal_sign)
+ {
+ *equal_sign = '\0';
+ ++equal_sign;
+ entry->flag = FLAG_ANY;
+ for (i = 0; i < sizeof (lib_types) / sizeof (lib_types [0]); ++i)
+ if (strcmp (equal_sign, lib_types[i].name) == 0)
+ {
+ entry->flag = lib_types[i].flag;
+ break;
+ }
+ if (entry->flag == FLAG_ANY)
+ error (0, 0, _("%s is not a known library type"), equal_sign);
+ }
+ else
+ {
+ entry->flag = FLAG_ANY;
+ }
+
+ /* Canonify path: for now only remove trailing slashes. */
+ i = strlen (entry->path) - 1;
+ while (entry->path[i] == '/' && i > 0)
+ {
+ entry->path [i] = '\0';
+ --i;
+ }
+
+ ptr = dir_entries;
+ prev = ptr;
+ while (ptr != NULL)
+ {
+ /* Check for duplicates. */
+ if (strcmp (ptr->path, entry->path) == 0)
+ {
+ if (opt_verbose)
+ error (0, 0, _("Path `%s' given more than once"), entry->path);
+ /* Use the newer information. */
+ ptr->flag = entry->flag;
+ free (entry);
+ break;
+ }
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ /* Is this the first entry? */
+ if (ptr == NULL && dir_entries == NULL)
+ dir_entries = entry;
+ else if (ptr == NULL)
+ prev->next = entry;
+}
+
+
+/* Create a symbolic link from soname to libname in directory path. */
+static void
+create_links (const char *path, const char *libname, const char *soname)
+{
+ char full_libname [PATH_MAX], full_soname [PATH_MAX];
+ struct stat stat_lib, stat_so, lstat_so;
+ int do_link = 1;
+ int do_remove = 1;
+ /* XXX: The logics in this function should be simplified. */
+
+ /* Get complete path. */
+ snprintf (full_libname, sizeof full_libname, "%s/%s", path, libname);
+ snprintf (full_soname, sizeof full_soname, "%s/%s", path, soname);
+
+ /* Does soname already exist and point to the right library? */
+ if (stat (full_soname, &stat_so) == 0)
+ {
+ if (stat (full_libname, &stat_lib))
+ {
+ error (0, 0, _("Can't stat %s\n"), full_libname);
+ return;
+ }
+ if (stat_lib.st_dev == stat_so.st_dev
+ && stat_lib.st_ino == stat_so.st_ino)
+ /* Link is already correct. */
+ do_link = 0;
+ else if (lstat (full_soname, &lstat_so) == 0
+ && !S_ISLNK (lstat_so.st_mode))
+ {
+ error (0, 0, _("%s is not a symbolic link\n"), full_soname);
+ do_link = 0;
+ do_remove = 0;
+ }
+ }
+ else if (lstat (full_soname, &lstat_so) != 0
+ || !S_ISLNK (lstat_so.st_mode))
+ /* Unless it is a stale symlink, there is no need to remove. */
+ do_remove = 0;
+
+ if (opt_verbose)
+ printf ("\t%s -> %s", soname, libname);
+
+ if (do_link && opt_link)
+ {
+ /* Remove old link. */
+ if (do_remove)
+ if (unlink (full_soname))
+ {
+ error (0, 0, _("Can't unlink %s"), full_soname);
+ do_link = 0;
+ }
+ /* Create symbolic link. */
+ if (do_link && symlink (libname, full_soname))
+ {
+ error (0, 0, _("Can't link %s to %s"), full_soname, libname);
+ do_link = 0;
+ }
+ if (opt_verbose)
+ {
+ if (do_link)
+ fputs (_(" (changed)\n"), stdout);
+ else
+ fputs (_(" (SKIPPED)\n"), stdout);
+ }
+ }
+ else if (opt_verbose)
+ fputs ("\n", stdout);
+}
+
+/* Read a whole directory and search for libraries.
+ The purpose is two-fold:
+ - search for libraries which will be added to the cache
+ - create symbolic links to the soname for each library
+
+ This has to be done separatly for each directory.
+
+ To keep track of which libraries to add to the cache and which
+ links to create, we save a list of all libraries.
+
+ The algorithm is basically:
+ for all libraries in the directory do
+ get soname of library
+ if soname is already in list
+ if new library is newer, replace entry
+ otherwise ignore this library
+ otherwise add library to list
+
+ For example, if the two libraries libxy.so.1.1 and libxy.so.1.2
+ exist and both have the same soname, e.g. libxy.so, a symbolic link
+ is created from libxy.so.1.2 (the newer one) to libxy.so.
+ libxy.so.1.2 and libxy.so are added to the cache - but not
+ libxy.so.1.1. */
+
+/* Information for one library. */
+struct dlib_entry
+{
+ char *name;
+ char *soname;
+ int flag;
+ int is_link;
+ struct dlib_entry *next;
+};
+
+
+static void
+search_dir (const struct dir_entry *entry)
+{
+ DIR *dir;
+ struct dirent *direntry;
+ char buf [PATH_MAX];
+ char *soname;
+ struct dlib_entry *dlibs;
+ struct dlib_entry *dlib_ptr;
+ int nchars;
+ struct stat stat_buf;
+ int is_link;
+
+ dlibs = NULL;
+
+ if (opt_verbose)
+ printf ("%s:\n", entry->path);
+
+ dir = opendir (entry->path);
+ if (dir == NULL)
+ {
+ if (opt_verbose)
+ error (0, errno, _("Can't open directory %s"), entry->path);
+ return;
+ }
+
+
+ while ((direntry = readdir (dir)) != NULL)
+ {
+ int flag;
+#ifdef _DIRENT_HAVE_D_TYPE
+ /* We only look at links and regular files. */
+ if (direntry->d_type != DT_UNKNOWN
+ && direntry->d_type != DT_LNK
+ && direntry->d_type != DT_REG)
+ continue;
+#endif /* _DIRENT_HAVE_D_TYPE */
+
+ /* Does this file look like a shared library? The dynamic
+ linker is also considered as shared library. */
+ if ((strncmp (direntry->d_name, "lib", 3) != 0
+ && strncmp (direntry->d_name, "ld-", 3) != 0)
+ || strstr (direntry->d_name, ".so") == NULL)
+ continue;
+ nchars = snprintf (buf, sizeof (buf), "%s/%s", entry->path,
+ direntry->d_name);
+ /* Check for overflow. */
+ if (nchars >= (int) sizeof (buf))
+ {
+ error (0, 0, _("buffer for snprintf too small for %s/%s--file is ignored\n"),
+ entry->path, direntry->d_name);
+ continue;
+ }
+ if (lstat (buf, &stat_buf))
+ {
+ error (0, errno, _("Can't lstat %s"), buf);
+ continue;
+ }
+ else if (!S_ISREG (stat_buf.st_mode) && !S_ISLNK (stat_buf.st_mode))
+ continue;
+
+ is_link = S_ISLNK (stat_buf.st_mode);
+
+ if (process_file (buf, direntry->d_name, &flag, &soname, is_link))
+ continue;
+
+ /* Links will just point to itself. */
+ if (is_link)
+ {
+ free (soname);
+ soname = xstrdup (direntry->d_name);
+ }
+
+ if (flag == FLAG_ELF
+ && (entry->flag == FLAG_ELF_LIBC5
+ || entry->flag == FLAG_ELF_LIBC6))
+ flag = entry->flag;
+ /* Some sanity checks to print warnings. */
+ if (opt_verbose)
+ {
+ if (flag == FLAG_ELF_LIBC5 && entry->flag != FLAG_ELF_LIBC5
+ && entry->flag != FLAG_ANY)
+ error (0, 0, _("libc5 library %s in wrong directory"), buf);
+ if (flag == FLAG_ELF_LIBC6 && entry->flag != FLAG_ELF_LIBC6
+ && entry->flag != FLAG_ANY)
+ error (0, 0, _("libc6 library %s in wrong directory"), buf);
+ if (flag == FLAG_LIBC4 && entry->flag != FLAG_LIBC4
+ && entry->flag != FLAG_ANY)
+ error (0, 0, _("libc4 library %s in wrong directory"), buf);
+ }
+
+ /* Add library to list. */
+ for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next)
+ {
+ /* Is soname already in list? */
+ if (strcmp (dlib_ptr->soname, soname) == 0)
+ {
+ /* Prefer a file to a link, otherwise check which one
+ is newer. */
+ if ((!is_link && dlib_ptr->is_link)
+ || (is_link == dlib_ptr->is_link
+ && cache_libcmp (dlib_ptr->name, direntry->d_name) < 0))
+ {
+ /* It's newer - add it. */
+ /* Flag should be the same - sanity check. */
+ if (dlib_ptr->flag != flag)
+ {
+ if (dlib_ptr->flag == FLAG_ELF
+ && (flag == FLAG_ELF_LIBC5 || flag == FLAG_ELF_LIBC6))
+ dlib_ptr->flag = flag;
+ else if ((dlib_ptr->flag == FLAG_ELF_LIBC5
+ || dlib_ptr->flag == FLAG_ELF_LIBC6)
+ && flag == FLAG_ELF)
+ dlib_ptr->flag = flag;
+ else
+ error (0, 0, _("libraries %s and %s in directory %s have same soname but different type."),
+ dlib_ptr->name, direntry->d_name, entry->path);
+ }
+ free (dlib_ptr->name);
+ dlib_ptr->name = xstrdup (direntry->d_name);
+ dlib_ptr->is_link = is_link;
+ }
+ /* Don't add this library, abort loop. */
+ /* Also free soname, since it's dynamically allocated. */
+ free (soname);
+ break;
+ }
+ }
+ /* Add the library if it's not already in. */
+ if (dlib_ptr == NULL)
+ {
+ dlib_ptr = (struct dlib_entry *)xmalloc (sizeof (struct dlib_entry));
+ dlib_ptr->name = xstrdup (direntry->d_name);
+ dlib_ptr->flag = flag;
+ dlib_ptr->soname = soname;
+ dlib_ptr->is_link = is_link;
+ /* Add at head of list. */
+ dlib_ptr->next = dlibs;
+ dlibs = dlib_ptr;
+ }
+ }
+
+ closedir (dir);
+
+ /* Now dlibs contains a list of all libs - add those to the cache
+ and created all symbolic links. */
+ for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next)
+ {
+ /* Don't create links to links. */
+ if (dlib_ptr->is_link == 0)
+ create_links (entry->path, dlib_ptr->name, dlib_ptr->soname);
+ if (opt_build_cache)
+ add_to_cache (entry->path, dlib_ptr->soname, dlib_ptr->flag);
+ }
+
+ /* Free all resources. */
+ while (dlibs)
+ {
+ dlib_ptr = dlibs;
+ free (dlib_ptr->soname);
+ free (dlib_ptr->name);
+ dlibs = dlibs->next;
+ free (dlib_ptr);
+ }
+}
+
+/* Search through all libraries. */
+static void
+search_dirs (void)
+{
+ struct dir_entry *entry;
+
+ for (entry = dir_entries; entry != NULL; entry = entry->next)
+ search_dir (entry);
+
+ /* Free all allocated memory. */
+ while (dir_entries)
+ {
+ entry = dir_entries;
+ dir_entries = dir_entries->next;
+ free (entry->path);
+ free (entry);
+ }
+}
+
+
+/* Parse configuration file. */
+static void
+parse_conf (const char *filename)
+{
+ FILE *file;
+ char *line = NULL;
+ size_t len = 0;
+
+ file = fopen (filename, "r");
+
+ if (file == NULL)
+ {
+ error (0, errno, _("Can't open configuration file %s"), filename);
+ return;
+ }
+
+ do
+ {
+ ssize_t n = getline (&line, &len, file);
+ if (n < 0)
+ break;
+
+ 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';
+
+ /* If the line is blank it is ignored. */
+ if (line[0] == '\0')
+ continue;
+
+ add_dir (line);
+ } while (!feof (file));
+
+ /* Free buffer and close file. */
+ free (line);
+ fclose (file);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ int remaining;
+
+ /* Parse and process arguments. */
+ argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+ /* Remaining arguments are additional libraries. */
+ if (remaining != argc)
+ {
+ int i;
+ for (i = remaining; i < argc; ++i)
+ add_dir (argv [i]);
+ }
+
+ if (cache_file == NULL)
+ cache_file = LD_SO_CACHE;
+
+ if (config_file == NULL)
+ config_file = LD_SO_CONF;
+
+ /* Chroot first. */
+ if (opt_chroot)
+ {
+ if (chroot (opt_chroot))
+ /* Report failure and exit program. */
+ error (EXIT_FAILURE, errno, _("Can't chroot to %s"), opt_chroot);
+ /* chroot doesn't change the working directory, let's play safe. */
+ if (chdir ("/"))
+ error (EXIT_FAILURE, errno, _("Can't chdir to /"));
+ }
+
+ if (opt_print_cache)
+ {
+ print_cache (cache_file);
+ exit (0);
+ }
+
+ if (opt_build_cache)
+ init_cache ();
+
+ if (!opt_only_cline)
+ {
+ /* Always add the standard search paths. */
+ add_dir ("/lib");
+ add_dir ("/usr/lib");
+
+ parse_conf (config_file);
+ }
+
+ search_dirs ();
+
+ if (opt_build_cache)
+ save_cache (cache_file);
+
+ return 0;
+}