ldconfig.c revision 92882
174462Salfred/*
274462Salfred * Copyright (c) 1993,1995 Paul Kranenburg
374462Salfred * All rights reserved.
474462Salfred *
574462Salfred * Redistribution and use in source and binary forms, with or without
674462Salfred * modification, are permitted provided that the following conditions
774462Salfred * are met:
874462Salfred * 1. Redistributions of source code must retain the above copyright
974462Salfred *    notice, this list of conditions and the following disclaimer.
1074462Salfred * 2. Redistributions in binary form must reproduce the above copyright
1174462Salfred *    notice, this list of conditions and the following disclaimer in the
1274462Salfred *    documentation and/or other materials provided with the distribution.
1374462Salfred * 3. All advertising materials mentioning features or use of this software
1474462Salfred *    must display the following acknowledgement:
1574462Salfred *      This product includes software developed by Paul Kranenburg.
1674462Salfred * 4. The name of the author may not be used to endorse or promote products
1774462Salfred *    derived from this software without specific prior written permission
1874462Salfred *
1974462Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2074462Salfred * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2174462Salfred * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2274462Salfred * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2374462Salfred * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2474462Salfred * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2574462Salfred * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2674462Salfred * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2774462Salfred * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2874462Salfred * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2974462Salfred */
3074462Salfred
3174462Salfred#ifndef lint
3274462Salfredstatic const char rcsid[] =
3374462Salfred  "$FreeBSD: head/sbin/ldconfig/ldconfig.c 92882 2002-03-21 13:14:21Z imp $";
3474462Salfred#endif /* not lint */
3574462Salfred
3674462Salfred#include <sys/param.h>
3774462Salfred#include <sys/types.h>
3874462Salfred#include <sys/stat.h>
3974462Salfred#include <sys/mman.h>
4074462Salfred#include <a.out.h>
4174462Salfred#include <ctype.h>
4274462Salfred#include <dirent.h>
4374462Salfred#include <elf-hints.h>
4474462Salfred#include <err.h>
4574462Salfred#include <errno.h>
4674462Salfred#include <fcntl.h>
4774462Salfred#include <link.h>
4874462Salfred#include <objformat.h>
4974462Salfred#include <stdio.h>
5074462Salfred#include <stdlib.h>
5174462Salfred#include <string.h>
5274462Salfred#include <unistd.h>
5374462Salfred
5474462Salfred#include "ldconfig.h"
5574462Salfred#include "shlib.h"
56173412Skevlo#include "support.h"
57173412Skevlo
58173412Skevlo#if DEBUG
59173412Skevlo/* test */
6074462Salfred#undef _PATH_LD_HINTS
6174462Salfred#define _PATH_LD_HINTS		"./ld.so.hints"
6274462Salfred#undef _PATH_ELF_HINTS
6374462Salfred#define _PATH_ELF_HINTS		"./ld-elf.so.hints"
6474462Salfred#endif
6574462Salfred
6674462Salfred#undef major
6774462Salfred#undef minor
6874462Salfred
6974462Salfredstatic int			verbose;
7074462Salfredstatic int			nostd;
7174462Salfredstatic int			justread;
7274462Salfredstatic int			merge;
7374462Salfredstatic int			rescan;
7474462Salfredstatic char			*hints_file;
7574462Salfred
7674462Salfredstruct shlib_list {
7774462Salfred	/* Internal list of shared libraries found */
78173412Skevlo	char			*name;
7974462Salfred	char			*path;
8074462Salfred	int			dewey[MAXDEWEY];
8174462Salfred	int			ndewey;
8274462Salfred#define major dewey[0]
8374462Salfred#define minor dewey[1]
8474462Salfred	struct shlib_list	*next;
8574462Salfred};
8674462Salfred
8774462Salfredstatic struct shlib_list	*shlib_head = NULL, **shlib_tail = &shlib_head;
8874462Salfredstatic char			*dir_list;
8974462Salfred
9074462Salfredstatic int		buildhints(void);
9174462Salfredstatic int		dodir(char *, int);
9274462Salfredint			dofile(char *, int);
9374462Salfredstatic void		enter(char *, char *, char *, int *, int);
9474462Salfredstatic void		listhints(void);
9574462Salfredstatic int		readhints(void);
9674462Salfredstatic void		usage(void);
9774462Salfred
9874462Salfredint
9974462Salfredmain(argc, argv)
10074462Salfredint	argc;
10174462Salfredchar	*argv[];
10274462Salfred{
10374462Salfred	int		i, c;
10474462Salfred	int		rval = 0;
10574462Salfred	char		objformat[32];
10674462Salfred	int		is_aout;
10774462Salfred
10874462Salfred	if (getobjformat(objformat, sizeof objformat, &argc, argv) == -1)
10974462Salfred		errx(1, "getobjformat failed: name too long");
11074462Salfred	if (strcmp(objformat, "aout") == 0)
11174462Salfred		is_aout = 1;
11274462Salfred	else if (strcmp(objformat, "elf") == 0)
11374462Salfred		is_aout = 0;
11474462Salfred	else
11574462Salfred		errx(1, "unknown object format \"%s\"", objformat);
11674462Salfred
11774462Salfred	hints_file = is_aout ? _PATH_LD_HINTS : _PATH_ELF_HINTS;
11874462Salfred	if (argc == 1)
11974462Salfred		rescan = 1;
12074462Salfred	else while((c = getopt(argc, argv, "Rf:imrsv")) != -1) {
12174462Salfred		switch (c) {
12274462Salfred		case 'R':
12374462Salfred			rescan = 1;
12474462Salfred			break;
12574462Salfred		case 'f':
12674462Salfred			hints_file = optarg;
12774462Salfred			break;
12874462Salfred		case 'i':
12974462Salfred			insecure = 1;
13074462Salfred			break;
13174462Salfred		case 'm':
13274462Salfred			merge = 1;
13374462Salfred			break;
13474462Salfred		case 'r':
13574462Salfred			justread = 1;
13674462Salfred			break;
13774462Salfred		case 's':
13874462Salfred			nostd = 1;
13974462Salfred			break;
14074462Salfred		case 'v':
14174462Salfred			verbose = 1;
14274462Salfred			break;
14374462Salfred		default:
14474462Salfred			usage();
14574462Salfred			break;
14674462Salfred		}
14774462Salfred	}
14874462Salfred
14974462Salfred	if (!is_aout) {
15074462Salfred		if (justread)
15174462Salfred			list_elf_hints(hints_file);
15274462Salfred		else
15374462Salfred			update_elf_hints(hints_file, argc - optind,
15474462Salfred			    argv + optind, merge || rescan);
15574462Salfred		return 0;
15674462Salfred	}
15774462Salfred
15874462Salfred	/* Here begins the aout libs processing */
15974462Salfred	dir_list = strdup("");
16074462Salfred
16174462Salfred	if (justread || merge || rescan) {
16274462Salfred		if ((rval = readhints()) != 0)
16374462Salfred			return rval;
16474462Salfred	}
16574462Salfred
16674462Salfred	if (!nostd && !merge && !rescan)
16774462Salfred		std_search_path();
16874462Salfred
16974462Salfred	/* Add any directories/files from the command line */
17074462Salfred	if (!justread) {
17174462Salfred		for (i = optind; i < argc; i++) {
17274462Salfred			struct stat stbuf;
17374462Salfred
17474462Salfred			if (stat(argv[i], &stbuf) == -1) {
17574462Salfred				warn("%s", argv[i]);
17674462Salfred				rval = -1;
17774462Salfred			} else if (strcmp(argv[i], "/usr/lib") == 0) {
17874462Salfred				warnx("WARNING! '%s' can not be used", argv[i]);
17974462Salfred				rval = -1;
18074462Salfred			} else {
18174462Salfred				/*
18274462Salfred				 * See if this is a directory-containing
18374462Salfred				 * file instead of a directory
18474462Salfred				 */
18574462Salfred				if (S_ISREG(stbuf.st_mode))
18674462Salfred					rval |= dofile(argv[i], 0);
18774462Salfred				else
18874462Salfred					add_search_path(argv[i]);
18974462Salfred			}
19074462Salfred		}
19174462Salfred	}
19274462Salfred
19374462Salfred	for (i = 0; i < n_search_dirs; i++) {
19474462Salfred		char *cp = concat(dir_list, *dir_list?":":"", search_dirs[i]);
19574462Salfred		free(dir_list);
19674462Salfred		dir_list = cp;
19774462Salfred	}
19874462Salfred
19974462Salfred	if (justread) {
20074462Salfred		listhints();
20174462Salfred		return 0;
20274462Salfred	}
20374462Salfred
20474462Salfred	for (i = 0; i < n_search_dirs; i++)
20574462Salfred		rval |= dodir(search_dirs[i], 1);
20674462Salfred
207104592Salfred	rval |= buildhints();
208104592Salfred
20974462Salfred	return rval;
21074462Salfred}
21174462Salfred
21274462Salfredstatic void
21374462Salfredusage()
21474462Salfred{
21574462Salfred	fprintf(stderr,
21674462Salfred	"usage: ldconfig [-aout | -elf] [-Rimrsv] [-f hints_file] [dir | file ...]\n");
21774462Salfred	exit(1);
21874462Salfred}
21974462Salfred
22074462Salfredint
22174462Salfreddofile(fname, silent)
22274462Salfredchar	*fname;
22374462Salfredint	silent;
22474462Salfred{
22574462Salfred	FILE *hfp;
22674462Salfred	char buf[MAXPATHLEN];
22774462Salfred	int rval = 0;
22874462Salfred	char *cp, *sp;
229104592Salfred
230104592Salfred	if ((hfp = fopen(fname, "r")) == NULL) {
23174462Salfred		warn("%s", fname);
23274462Salfred		return -1;
23374462Salfred	}
234
235	while (fgets(buf, sizeof(buf), hfp)) {
236		cp = buf;
237		while (isspace(*cp))
238			cp++;
239		if (*cp == '#' || *cp == '\0')
240			continue;
241		sp = cp;
242		while (!isspace(*cp) && *cp != '\0')
243			cp++;
244
245		if (*cp != '\n') {
246			*cp = '\0';
247			warnx("%s: trailing characters ignored", sp);
248		}
249
250		*cp = '\0';
251
252		rval |= dodir(sp, silent);
253	}
254
255	(void)fclose(hfp);
256	return rval;
257}
258
259int
260dodir(dir, silent)
261char	*dir;
262int	silent;
263{
264	DIR		*dd;
265	struct dirent	*dp;
266	char		name[MAXPATHLEN];
267	int		dewey[MAXDEWEY], ndewey;
268
269	if ((dd = opendir(dir)) == NULL) {
270		if (silent && errno == ENOENT)	/* Ignore the error */
271			return 0;
272		warn("%s", dir);
273		return -1;
274	}
275
276	while ((dp = readdir(dd)) != NULL) {
277		int n;
278		char *cp;
279
280		/* Check for `lib' prefix */
281		if (dp->d_name[0] != 'l' ||
282		    dp->d_name[1] != 'i' ||
283		    dp->d_name[2] != 'b')
284			continue;
285
286		/* Copy the entry minus prefix */
287		(void)strcpy(name, dp->d_name + 3);
288		n = strlen(name);
289		if (n < 4)
290			continue;
291
292		/* Find ".so." in name */
293		for (cp = name + n - 4; cp > name; --cp) {
294			if (cp[0] == '.' &&
295			    cp[1] == 's' &&
296			    cp[2] == 'o' &&
297			    cp[3] == '.')
298				break;
299		}
300		if (cp <= name)
301			continue;
302
303		*cp = '\0';
304		if (!isdigit(*(cp+4)))
305			continue;
306
307		bzero((caddr_t)dewey, sizeof(dewey));
308		ndewey = getdewey(dewey, cp + 4);
309		if (ndewey < 2)
310			continue;
311		enter(dir, dp->d_name, name, dewey, ndewey);
312	}
313
314	closedir(dd);
315	return 0;
316}
317
318static void
319enter(dir, file, name, dewey, ndewey)
320char	*dir, *file, *name;
321int	dewey[], ndewey;
322{
323	struct shlib_list	*shp;
324
325	for (shp = shlib_head; shp; shp = shp->next) {
326		if (strcmp(name, shp->name) != 0 || major != shp->major)
327			continue;
328
329		/* Name matches existing entry */
330		if (cmpndewey(dewey, ndewey, shp->dewey, shp->ndewey) > 0) {
331
332			/* Update this entry with higher versioned lib */
333			if (verbose)
334				printf("Updating lib%s.%d.%d to %s/%s\n",
335					shp->name, shp->major, shp->minor,
336					dir, file);
337
338			free(shp->name);
339			shp->name = strdup(name);
340			free(shp->path);
341			shp->path = concat(dir, "/", file);
342			bcopy(dewey, shp->dewey, sizeof(shp->dewey));
343			shp->ndewey = ndewey;
344		}
345		break;
346	}
347
348	if (shp)
349		/* Name exists: older version or just updated */
350		return;
351
352	/* Allocate new list element */
353	if (verbose)
354		printf("Adding %s/%s\n", dir, file);
355
356	shp = (struct shlib_list *)xmalloc(sizeof *shp);
357	shp->name = strdup(name);
358	shp->path = concat(dir, "/", file);
359	bcopy(dewey, shp->dewey, MAXDEWEY);
360	shp->ndewey = ndewey;
361	shp->next = NULL;
362
363	*shlib_tail = shp;
364	shlib_tail = &shp->next;
365}
366
367
368int
369hinthash(cp, vmajor)
370char	*cp;
371int	vmajor;
372{
373	int	k = 0;
374
375	while (*cp)
376		k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
377
378	k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
379
380	return k;
381}
382
383int
384buildhints()
385{
386	struct hints_header	hdr;
387	struct hints_bucket	*blist;
388	struct shlib_list	*shp;
389	char			*strtab;
390	int			i, n, str_index = 0;
391	int			strtab_sz = 0;	/* Total length of strings */
392	int			nhints = 0;	/* Total number of hints */
393	int			fd;
394	char			*tmpfile;
395
396	for (shp = shlib_head; shp; shp = shp->next) {
397		strtab_sz += 1 + strlen(shp->name);
398		strtab_sz += 1 + strlen(shp->path);
399		nhints++;
400	}
401
402	/* Fill hints file header */
403	hdr.hh_magic = HH_MAGIC;
404	hdr.hh_version = LD_HINTS_VERSION_2;
405	hdr.hh_nbucket = 1 * nhints;
406	n = hdr.hh_nbucket * sizeof(struct hints_bucket);
407	hdr.hh_hashtab = sizeof(struct hints_header);
408	hdr.hh_strtab = hdr.hh_hashtab + n;
409	hdr.hh_dirlist = strtab_sz;
410	strtab_sz += 1 + strlen(dir_list);
411	hdr.hh_strtab_sz = strtab_sz;
412	hdr.hh_ehints = hdr.hh_strtab + hdr.hh_strtab_sz;
413
414	if (verbose)
415		printf("Totals: entries %d, buckets %ld, string size %d\n",
416			nhints, (long)hdr.hh_nbucket, strtab_sz);
417
418	/* Allocate buckets and string table */
419	blist = (struct hints_bucket *)xmalloc(n);
420	bzero((char *)blist, n);
421	for (i = 0; i < hdr.hh_nbucket; i++)
422		/* Empty all buckets */
423		blist[i].hi_next = -1;
424
425	strtab = (char *)xmalloc(strtab_sz);
426
427	/* Enter all */
428	for (shp = shlib_head; shp; shp = shp->next) {
429		struct hints_bucket	*bp;
430
431		bp = blist +
432		  (hinthash(shp->name, shp->major) % hdr.hh_nbucket);
433
434		if (bp->hi_pathx) {
435			int	i;
436
437			for (i = 0; i < hdr.hh_nbucket; i++) {
438				if (blist[i].hi_pathx == 0)
439					break;
440			}
441			if (i == hdr.hh_nbucket) {
442				warnx("bummer!");
443				return -1;
444			}
445			while (bp->hi_next != -1)
446				bp = &blist[bp->hi_next];
447			bp->hi_next = i;
448			bp = blist + i;
449		}
450
451		/* Insert strings in string table */
452		bp->hi_namex = str_index;
453		strcpy(strtab + str_index, shp->name);
454		str_index += 1 + strlen(shp->name);
455
456		bp->hi_pathx = str_index;
457		strcpy(strtab + str_index, shp->path);
458		str_index += 1 + strlen(shp->path);
459
460		/* Copy versions */
461		bcopy(shp->dewey, bp->hi_dewey, sizeof(bp->hi_dewey));
462		bp->hi_ndewey = shp->ndewey;
463	}
464
465	/* Copy search directories */
466	strcpy(strtab + str_index, dir_list);
467	str_index += 1 + strlen(dir_list);
468
469	/* Sanity check */
470	if (str_index != strtab_sz) {
471		errx(1, "str_index(%d) != strtab_sz(%d)", str_index, strtab_sz);
472	}
473
474	tmpfile = concat(hints_file, ".XXXXXXXXXX", "");
475	umask(0);	/* Create with exact permissions */
476	if ((fd = mkstemp(tmpfile)) == -1) {
477		warn("%s", tmpfile);
478		return -1;
479	}
480	fchmod(fd, 0444);
481
482	if (write(fd, &hdr, sizeof(struct hints_header)) !=
483						sizeof(struct hints_header)) {
484		warn("%s", hints_file);
485		return -1;
486	}
487	if (write(fd, blist, hdr.hh_nbucket * sizeof(struct hints_bucket)) !=
488				hdr.hh_nbucket * sizeof(struct hints_bucket)) {
489		warn("%s", hints_file);
490		return -1;
491	}
492	if (write(fd, strtab, strtab_sz) != strtab_sz) {
493		warn("%s", hints_file);
494		return -1;
495	}
496	if (close(fd) != 0) {
497		warn("%s", hints_file);
498		return -1;
499	}
500
501	/* Install it */
502	if (unlink(hints_file) != 0 && errno != ENOENT) {
503		warn("%s", hints_file);
504		return -1;
505	}
506
507	if (rename(tmpfile, hints_file) != 0) {
508		warn("%s", hints_file);
509		return -1;
510	}
511
512	return 0;
513}
514
515static int
516readhints()
517{
518	int			fd;
519	void			*addr;
520	long			fsize;
521	long			msize;
522	struct hints_header	*hdr;
523	struct hints_bucket	*blist;
524	char			*strtab;
525	struct shlib_list	*shp;
526	int			i;
527
528	if ((fd = open(hints_file, O_RDONLY, 0)) == -1) {
529		warn("%s", hints_file);
530		return -1;
531	}
532
533	msize = PAGE_SIZE;
534	addr = mmap(0, msize, PROT_READ, MAP_COPY, fd, 0);
535
536	if (addr == MAP_FAILED) {
537		warn("%s", hints_file);
538		return -1;
539	}
540
541	hdr = (struct hints_header *)addr;
542	if (HH_BADMAG(*hdr)) {
543		warnx("%s: bad magic: %lo", hints_file,
544			(unsigned long)hdr->hh_magic);
545		return -1;
546	}
547
548	if (hdr->hh_version != LD_HINTS_VERSION_1 &&
549	    hdr->hh_version != LD_HINTS_VERSION_2) {
550		warnx("unsupported version: %ld", (long)hdr->hh_version);
551		return -1;
552	}
553
554	if (hdr->hh_ehints > msize) {
555		fsize = hdr->hh_ehints;
556		munmap(addr, msize);
557		addr = mmap(0, fsize, PROT_READ, MAP_COPY, fd, 0);
558		if (addr == MAP_FAILED) {
559			warn("%s", hints_file);
560			return -1;
561		}
562		hdr = (struct hints_header *)addr;
563	}
564	close(fd);
565
566	blist = (struct hints_bucket *)(addr + hdr->hh_hashtab);
567	strtab = (char *)(addr + hdr->hh_strtab);
568
569	if (hdr->hh_version >= LD_HINTS_VERSION_2)
570		add_search_path(strtab + hdr->hh_dirlist);
571	else if (rescan)
572		errx(1, "%s too old and does not contain the search path",
573			hints_file);
574
575	if (rescan)
576		return 0;
577
578	for (i = 0; i < hdr->hh_nbucket; i++) {
579		struct hints_bucket	*bp = &blist[i];
580
581		/* Sanity check */
582		if (bp->hi_namex >= hdr->hh_strtab_sz) {
583			warnx("bad name index: %#x", bp->hi_namex);
584			return -1;
585		}
586		if (bp->hi_pathx >= hdr->hh_strtab_sz) {
587			warnx("bad path index: %#x", bp->hi_pathx);
588			return -1;
589		}
590
591		/* Allocate new list element */
592		shp = (struct shlib_list *)xmalloc(sizeof *shp);
593		shp->name = strdup(strtab + bp->hi_namex);
594		shp->path = strdup(strtab + bp->hi_pathx);
595		bcopy(bp->hi_dewey, shp->dewey, sizeof(shp->dewey));
596		shp->ndewey = bp->hi_ndewey;
597		shp->next = NULL;
598
599		*shlib_tail = shp;
600		shlib_tail = &shp->next;
601	}
602
603	return 0;
604}
605
606static void
607listhints()
608{
609	struct shlib_list	*shp;
610	int			i;
611
612	printf("%s:\n", hints_file);
613	printf("\tsearch directories: %s\n", dir_list);
614
615	for (i = 0, shp = shlib_head; shp; i++, shp = shp->next)
616		printf("\t%d:-l%s.%d.%d => %s\n",
617			i, shp->name, shp->major, shp->minor, shp->path);
618
619	return;
620}
621