elfhints.c revision 50476
1139823Simp/*-
21541Srgrimes * Copyright (c) 1998 John D. Polstra
31541Srgrimes * All rights reserved.
41541Srgrimes *
51541Srgrimes * Redistribution and use in source and binary forms, with or without
61541Srgrimes * modification, are permitted provided that the following conditions
71541Srgrimes * are met:
81541Srgrimes * 1. Redistributions of source code must retain the above copyright
91541Srgrimes *    notice, this list of conditions and the following disclaimer.
101541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111541Srgrimes *    notice, this list of conditions and the following disclaimer in the
121541Srgrimes *    documentation and/or other materials provided with the distribution.
131541Srgrimes *
141541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
151541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241541Srgrimes * SUCH DAMAGE.
251541Srgrimes *
261541Srgrimes * $FreeBSD: head/sbin/ldconfig/elfhints.c 50476 1999-08-28 00:22:10Z peter $
271541Srgrimes */
281541Srgrimes
291541Srgrimes#include <sys/param.h>
3050477Speter#include <sys/mman.h>
311541Srgrimes#include <sys/stat.h>
321541Srgrimes
332168Spaul#include <ctype.h>
344507Sbde#include <dirent.h>
352168Spaul#include <elf.h>
36104355Smike#include <err.h>
37104355Smike#include <errno.h>
3897024Siedowse#include <fcntl.h>
3979103Sbrooks#include <stdio.h>
4097024Siedowse#include <stdlib.h>
4179103Sbrooks#include <string.h>
42104355Smike#include <unistd.h>
431541Srgrimes
4434750Speter#include "ldconfig.h"
4572093Sasmodai
4634750Speter#define MAXDIRS		1024		/* Maximum directories in path */
4755205Speter#define MAXFILESIZE	(16*1024)	/* Maximum hints file size */
4834750Speter
4934750Speterstatic void	add_dir(const char *, const char *);
5034750Speterstatic void	read_dirs_from_file(const char *, const char *);
5179103Sbrooksstatic void	read_elf_hints(const char *, int);
52104355Smikestatic void	write_elf_hints(const char *);
5379103Sbrooks
5434750Speterstatic const char	*dirs[MAXDIRS];
5579103Sbrooksstatic int		 ndirs;
5679103Sbrooks
5779103Sbrooksstatic void
58104355Smikeadd_dir(const char *hintsfile, const char *name)
59104355Smike{
60104355Smike	int	i;
61121816Sbrooks
62104355Smike	for (i = 0;  i < ndirs;  i++)
63104355Smike		if (strcmp(dirs[i], name) == 0)
64104355Smike			return;
6579103Sbrooks	if (ndirs >= MAXDIRS)
6679103Sbrooks		errx(1, "\"%s\": Too many directories in path", hintsfile);
6779103Sbrooks	dirs[ndirs++] = name;
6879103Sbrooks}
6979103Sbrooks
7079103Sbrooksvoid
7179103Sbrookslist_elf_hints(const char *hintsfile)
7279103Sbrooks{
7379103Sbrooks	int	i;
7479103Sbrooks	int	nlibs;
7579103Sbrooks
7619079Sfenner	read_elf_hints(hintsfile, 1);
7719079Sfenner	printf("%s:\n", hintsfile);
7819079Sfenner	printf("\tsearch directories:");
799457Sjoerg	for (i = 0;  i < ndirs;  i++)
809457Sjoerg		printf("%c%s", i == 0 ? ' ' : ':', dirs[i]);
819457Sjoerg	printf("\n");
829457Sjoerg
839457Sjoerg	nlibs = 0;
849457Sjoerg	for (i = 0;  i < ndirs;  i++) {
85128871Sandre		DIR		*dirp;
8617352Swollman		struct dirent	*dp;
8717352Swollman
88134609Sbrooks		if ((dirp = opendir(dirs[i])) == NULL)
899457Sjoerg			continue;
909457Sjoerg		while ((dp = readdir(dirp)) != NULL) {
919457Sjoerg			int		 len;
929457Sjoerg			int		 namelen;
939457Sjoerg			const char	*name;
949457Sjoerg			const char	*vers;
959457Sjoerg
969457Sjoerg			/* Name can't be shorter than "libx.so.0" */
979457Sjoerg			if ((len = strlen(dp->d_name)) < 9 ||
989457Sjoerg			    strncmp(dp->d_name, "lib", 3) != 0)
999457Sjoerg				continue;
1009457Sjoerg			name = dp->d_name + 3;
1019457Sjoerg			vers = dp->d_name + len;
1029457Sjoerg			while (vers > dp->d_name && isdigit(*(vers-1)))
1039457Sjoerg				vers--;
10458698Sjlemon			if (vers == dp->d_name + len)
105142501Sbrooks				continue;
106134933Sbrooks			if (vers < dp->d_name + 4 ||
107134933Sbrooks			    strncmp(vers - 4, ".so.", 4) != 0)
108134933Sbrooks				continue;
10916287Sgpalmer
1109457Sjoerg			/* We have a valid shared library name. */
1119457Sjoerg			namelen = (vers - 4) - name;
1121541Srgrimes			printf("\t%d:-l%.*s.%s => %s/%s\n", nlibs,
1131541Srgrimes			    namelen, name, vers, dirs[i], dp->d_name);
1141541Srgrimes			nlibs++;
1151541Srgrimes		}
1161541Srgrimes		closedir(dirp);
11747777Sphk	}
1181541Srgrimes}
1191541Srgrimes
1201541Srgrimesstatic void
1211541Srgrimesread_dirs_from_file(const char *hintsfile, const char *listfile)
122137824Sjmg{
1231541Srgrimes	FILE	*fp;
1241541Srgrimes	char	 buf[MAXPATHLEN];
1251541Srgrimes	int	 linenum;
1261541Srgrimes
1273274Swollman	if ((fp = fopen(listfile, "r")) == NULL)
1281541Srgrimes		err(1, "%s", listfile);
12987902Sluigi
130102099Ssobomax	linenum = 0;
131104044Sphk	while (fgets(buf, sizeof buf, fp) != NULL) {
132120626Sru		char	*cp, *sp;
133132712Srwatson
13487902Sluigi		linenum++;
1351541Srgrimes		cp = buf;
1361541Srgrimes		/* Skip leading white space. */
1371541Srgrimes		while (isspace(*cp))
138102526Ssobomax			cp++;
139102526Ssobomax		if (*cp == '#' || *cp == '\0')
1401541Srgrimes			continue;
141106925Ssam		sp = cp;
142128871Sandre		/* Advance over the directory name. */
143128871Sandre		while (!isspace(*cp) && *cp != '\0')
144128871Sandre			cp++;
145128871Sandre		/* Terminate the string and skip trailing white space. */
146128871Sandre		if (*cp != '\0') {
147128871Sandre			*cp++ = '\0';
148128871Sandre			while (isspace(*cp))
149106925Ssam				cp++;
150106925Ssam		}
151106925Ssam		/* Now we had better be at the end of the line. */
152106925Ssam		if (*cp != '\0')
153106925Ssam			warnx("%s:%d: trailing characters ignored",
154106925Ssam			    listfile, linenum);
155106925Ssam
15683624Sjlemon		if ((sp = strdup(sp)) == NULL)
15783636Sjlemon			errx(1, "Out of memory");
15883636Sjlemon		add_dir(hintsfile, sp);
15983636Sjlemon	}
160106925Ssam
161106925Ssam	fclose(fp);
162106925Ssam}
163128113Sru
16483624Sjlemonstatic void
16583636Sjlemonread_elf_hints(const char *hintsfile, int must_exist)
16683636Sjlemon{
1671541Srgrimes	int	 		 fd;
1681541Srgrimes	struct stat		 s;
1691541Srgrimes	void			*mapbase;
1701541Srgrimes	struct elfhints_hdr	*hdr;
1711541Srgrimes	char			*strtab;
1721541Srgrimes	char			*dirlist;
1731541Srgrimes	char			*p;
1741541Srgrimes
1751541Srgrimes	if ((fd = open(hintsfile, O_RDONLY)) == -1) {
17672093Sasmodai		if (errno == ENOENT && !must_exist)
1771541Srgrimes			return;
1781541Srgrimes		err(1, "Cannot open \"%s\"", hintsfile);
1791541Srgrimes	}
1801541Srgrimes	if (fstat(fd, &s) == -1)
1811541Srgrimes		err(1, "Cannot stat \"%s\"", hintsfile);
1821541Srgrimes	if (s.st_size > MAXFILESIZE)
1831541Srgrimes		errx(1, "\"%s\" is unreasonably large", hintsfile);
1841541Srgrimes	/*
1851541Srgrimes	 * We use a read-write, private mapping so that we can null-terminate
1861541Srgrimes	 * some strings in it without affecting the underlying file.
1871541Srgrimes	 */
1881541Srgrimes	mapbase = mmap(NULL, s.st_size, PROT_READ|PROT_WRITE,
1891541Srgrimes	    MAP_PRIVATE, fd, 0);
19072093Sasmodai	if (mapbase == MAP_FAILED)
1911541Srgrimes		err(1, "Cannot mmap \"%s\"", hintsfile);
1921541Srgrimes	close(fd);
1931541Srgrimes
1941541Srgrimes	hdr = (struct elfhints_hdr *)mapbase;
1951541Srgrimes	if (hdr->magic != ELFHINTS_MAGIC)
1961541Srgrimes		errx(1, "\"%s\": invalid file format", hintsfile);
1971541Srgrimes	if (hdr->version != 1)
1981541Srgrimes		errx(1, "\"%s\": unrecognized file version (%d)", hintsfile,
19921666Swollman		    hdr->version);
20021666Swollman
20121666Swollman	strtab = (char *)mapbase + hdr->strtab;
20221666Swollman	dirlist = strtab + hdr->dirlist;
20321666Swollman
20472093Sasmodai	if (*dirlist != '\0')
20521666Swollman		while ((p = strsep(&dirlist, ":")) != NULL)
20621666Swollman			add_dir(hintsfile, p);
20721666Swollman}
20821666Swollman
20921666Swollmanvoid
21021666Swollmanupdate_elf_hints(const char *hintsfile, int argc, char **argv, int merge)
21121666Swollman{
21289498Sru	int	i;
21389498Sru
21489498Sru	if (merge)
21589498Sru		read_elf_hints(hintsfile, 0);
21689498Sru	for (i = 0;  i < argc;  i++) {
21789498Sru		struct stat	s;
21889498Sru
21989498Sru		if (stat(argv[i], &s) == -1)
22089498Sru			err(1, "%s", argv[i]);
22189498Sru		if (S_ISREG(s.st_mode))
22289498Sru			read_dirs_from_file(hintsfile, argv[i]);
22389498Sru		else
22489498Sru			add_dir(hintsfile, argv[i]);
22589498Sru	}
22689498Sru	write_elf_hints(hintsfile);
2271541Srgrimes}
2281541Srgrimes
2291541Srgrimesstatic void
2301541Srgrimeswrite_elf_hints(const char *hintsfile)
2311541Srgrimes{
2321541Srgrimes	struct elfhints_hdr	 hdr;
2331541Srgrimes	char			*tempname;
2341541Srgrimes	int			 fd;
2351541Srgrimes	FILE			*fp;
2361541Srgrimes	int			 i;
2371541Srgrimes
23844144Sphk	if (asprintf(&tempname, "%s.XXXXXX", hintsfile) == -1)
23985079Sjlemon		errx(1, "Out of memory");
2401541Srgrimes	if ((fd = mkstemp(tempname)) ==  -1)
2411941Sdg		err(1, "mkstemp(%s)", tempname);
2425184Swollman	if (fchmod(fd, 0444) == -1)
24325434Speter		err(1, "fchmod(%s)", tempname);
2441541Srgrimes	if ((fp = fdopen(fd, "wb")) == NULL)
24583624Sjlemon		err(1, "fdopen(%s)", tempname);
2461541Srgrimes
2471541Srgrimes	hdr.magic = ELFHINTS_MAGIC;
2481541Srgrimes	hdr.version = 1;
2491541Srgrimes	hdr.strtab = sizeof hdr;
250102052Ssobomax	hdr.strsize = 0;
251102052Ssobomax	hdr.dirlist = 0;
2521541Srgrimes	memset(hdr.spare, 0, sizeof hdr.spare);
2531941Sdg
2545187Sdg	/* Count up the size of the string table. */
25525434Speter	if (ndirs > 0) {
2561541Srgrimes		hdr.strsize += strlen(dirs[0]);
25783624Sjlemon		for (i = 1;  i < ndirs;  i++)
25883624Sjlemon			hdr.strsize += 1 + strlen(dirs[i]);
25985079Sjlemon	}
2601541Srgrimes	hdr.dirlistlen = hdr.strsize;
2611541Srgrimes	hdr.strsize++;	/* For the null terminator */
26232491Swollman
26332491Swollman	/* Write the header. */
26432491Swollman	if (fwrite(&hdr, 1, sizeof hdr, fp) != sizeof hdr)
26532491Swollman		err(1, "%s: write error", tempname);
26632491Swollman	/* Write the strings. */
2671541Srgrimes	if (ndirs > 0) {
2681541Srgrimes		if (fputs(dirs[0], fp) == EOF)
2691541Srgrimes			err(1, "%s: write error", tempname);
2701541Srgrimes		for (i = 1;  i < ndirs;  i++)
2711541Srgrimes			if (fprintf(fp, ":%s", dirs[i]) < 0)
2721541Srgrimes				err(1, "%s: write error", tempname);
2731541Srgrimes	}
27425434Speter	if (putc('\0', fp) == EOF || fclose(fp) == EOF)
27525434Speter		err(1, "%s: write error", tempname);
27625434Speter
27725434Speter	if (rename(tempname, hintsfile) == -1)
27825434Speter		err(1, "rename %s to %s", tempname, hintsfile);
27925434Speter	free(tempname);
28025434Speter}
28125434Speter