ldconfig.c revision 1.29 1 /* $NetBSD: ldconfig.c,v 1.29 2000/05/27 17:06:34 matt Exp $ */
2
3 /*-
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Paul Kranenburg.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <sys/param.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/file.h>
43 #include <sys/time.h>
44 #include <sys/mman.h>
45 #include <a.out.h>
46 #include <ctype.h>
47 #include <dirent.h>
48 #include <err.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55 #include <link.h>
56
57 #include "shlib.h"
58
59 #define _PATH_LD_SO_CONF "/etc/ld.so.conf"
60
61 #undef major
62 #undef minor
63
64 extern char *__progname;
65
66 static int verbose;
67 static int nostd;
68 static int noconf;
69 static int justread;
70 static int merge;
71
72 struct shlib_list {
73 /* Internal list of shared libraries found */
74 char *name;
75 char *path;
76 int dewey[MAXDEWEY];
77 int ndewey;
78 #define major dewey[0]
79 #define minor dewey[1]
80 struct shlib_list *next;
81 };
82
83 static struct shlib_list *shlib_head = NULL, **shlib_tail = &shlib_head;
84 static char *dir_list;
85
86 static void enter __P((char *, char *, char *, int *, int));
87 static int dodir __P((char *, int, int));
88 static int do_conf __P((void));
89 static int buildhints __P((void));
90 static int readhints __P((void));
91 static void listhints __P((void));
92 static int hinthash __P((char *, int, int));
93 int main __P((int, char *[]));
94
95 int
96 main(argc, argv)
97 int argc;
98 char *argv[];
99 {
100 int i, c;
101 int rval = 0;
102
103 while ((c = getopt(argc, argv, "cmrsSv")) != -1) {
104 switch (c) {
105 case 'c':
106 noconf = 1;
107 break;
108 case 'm':
109 merge = 1;
110 break;
111 case 'r':
112 justread = 1;
113 break;
114 case 's':
115 nostd = 1;
116 noconf = 1;
117 break;
118 case 'S':
119 nostd = 1;
120 break;
121 case 'v':
122 verbose = 1;
123 break;
124 default:
125 errx(1, "Usage: %s [-c][-m][-r][-s][-S][-v][dir ...]",
126 __progname);
127 break;
128 }
129 }
130
131 dir_list = xmalloc(1);
132 *dir_list = '\0';
133
134 if (justread || merge) {
135 if ((rval = readhints()) != 0)
136 return (rval);
137 if (justread) {
138 listhints();
139 return (rval);
140 }
141 }
142
143 if (!nostd && !merge)
144 std_search_path();
145
146 for (i = 0; i < n_search_dirs; i++)
147 rval |= dodir(search_dirs[i], 1, 0);
148
149 if (!noconf && !merge)
150 rval |= do_conf();
151
152 for (i = optind; i < argc; i++) {
153 rval |= dodir(argv[i], 0, 1);
154 }
155
156 rval |= buildhints();
157
158 return (rval);
159 }
160
161 int
162 do_conf ()
163 {
164 FILE *conf;
165 char *line, *c;
166 char *cline = NULL;
167 size_t len;
168 int rval = 0;
169
170 if ((conf = fopen(_PATH_LD_SO_CONF, "r")) == NULL) {
171 if (verbose) {
172 warnx("can't open `%s'", _PATH_LD_SO_CONF);
173 }
174 return (0);
175 }
176
177 while ((line = fgetln(conf, &len)) != NULL) {
178 if (*line != '/')
179 continue;
180
181 if (line[len-1] == '\n') {
182 line[--len] = '\0';
183 } else {
184 cline = xmalloc(len+1);
185 memcpy(cline, line, len);
186 line = cline;
187 line[len] = '\0';
188 }
189
190 while (isblank(*line)) { line++; len--; }
191 if ((c = strchr(line, '#')) == NULL)
192 c = line + len;
193 while (--c >= line && isblank(*c)) continue;
194 if (c >= line) {
195 *++c = '\0';
196 rval |= dodir(line, 0, 1);
197 }
198
199 if (cline) {
200 free(cline);
201 cline = NULL;
202 }
203 }
204
205 (void) fclose(conf);
206
207 return (rval);
208 }
209
210 int
211 dodir(dir, silent, update_dir_list)
212 char *dir;
213 int silent;
214 int update_dir_list;
215 {
216 DIR *dd;
217 struct dirent *dp;
218 char name[MAXPATHLEN];
219 int dewey[MAXDEWEY], ndewey;
220
221 if ((dd = opendir(dir)) == NULL) {
222 if (!silent || errno != ENOENT)
223 warn("%s", dir);
224 return (-1);
225 }
226
227 if (update_dir_list) {
228 /* Check for duplicates? */
229 char *cp = concat(dir_list, *dir_list?":":"", dir);
230 free(dir_list);
231 dir_list = cp;
232 }
233
234 while ((dp = readdir(dd)) != NULL) {
235 int n;
236 char *cp, *path;
237 FILE *fp;
238 struct exec ex;
239
240 /* Check for `lib' prefix */
241 if (dp->d_name[0] != 'l' ||
242 dp->d_name[1] != 'i' ||
243 dp->d_name[2] != 'b')
244 continue;
245
246 /* Copy the entry minus prefix */
247 (void)strcpy(name, dp->d_name + 3);
248 n = strlen(name);
249 if (n < 4)
250 continue;
251
252 /* Find ".so." in name */
253 for (cp = name + n - 4; cp > name; --cp) {
254 if (cp[0] == '.' &&
255 cp[1] == 's' &&
256 cp[2] == 'o' &&
257 cp[3] == '.')
258 break;
259 }
260 if (cp <= name)
261 continue;
262
263 path = concat(dir, "/", dp->d_name);
264 fp = fopen(path, "r");
265 free(path);
266 if (fp == NULL)
267 continue;
268 n = fread(&ex, 1, sizeof(ex), fp);
269 fclose(fp);
270 if (n != sizeof(ex)
271 || N_GETMAGIC(ex) != ZMAGIC
272 || (N_GETFLAG(ex) & EX_DYNAMIC) == 0)
273 continue;
274
275 *cp = '\0';
276 if (!isdigit(*(cp+4)))
277 continue;
278
279 memset(dewey, 0, sizeof(dewey));
280 ndewey = getdewey(dewey, cp + 4);
281 enter(dir, dp->d_name, name, dewey, ndewey);
282 }
283
284 (void) closedir(dd);
285
286 return (0);
287 }
288
289 static void
290 enter(dir, file, name, dewey, ndewey)
291 char *dir, *file, *name;
292 int dewey[], ndewey;
293 {
294 struct shlib_list *shp;
295
296 for (shp = shlib_head; shp; shp = shp->next) {
297 if (strcmp(name, shp->name) != 0 || major != shp->major)
298 continue;
299
300 /* Name matches existing entry */
301 if (cmpndewey(dewey, ndewey, shp->dewey, shp->ndewey) > 0) {
302
303 /* Update this entry with higher versioned lib */
304 if (verbose)
305 printf("Updating lib%s.%d.%d to %s/%s\n",
306 shp->name, shp->major, shp->minor,
307 dir, file);
308
309 free(shp->name);
310 shp->name = strdup(name);
311 free(shp->path);
312 shp->path = concat(dir, "/", file);
313 memcpy(shp->dewey, dewey, sizeof(shp->dewey));
314 shp->ndewey = ndewey;
315 }
316 break;
317 }
318
319 if (shp)
320 /* Name exists: older version or just updated */
321 return;
322
323 /* Allocate new list element */
324 if (verbose)
325 printf("Adding %s/%s\n", dir, file);
326
327 shp = (struct shlib_list *)xmalloc(sizeof *shp);
328 shp->name = strdup(name);
329 shp->path = concat(dir, "/", file);
330 memcpy(shp->dewey, dewey, MAXDEWEY);
331 shp->ndewey = ndewey;
332 shp->next = NULL;
333
334 *shlib_tail = shp;
335 shlib_tail = &shp->next;
336 }
337
338
339 #if DEBUG
340 /* test */
341 #undef _PATH_LD_HINTS
342 #define _PATH_LD_HINTS "./ld.so.hints"
343 #endif
344
345 /* XXX - should be a common function with rtld.c */
346 int
347 hinthash(cp, vmajor, vminor)
348 char *cp;
349 int vmajor, vminor;
350 {
351 int k = 0;
352
353 while (*cp)
354 k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
355
356 k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
357 #if 0
358 k = (((k << 1) + (k >> 14)) ^ (vminor*167)) & 0x3fff;
359 #endif
360
361 return (k);
362 }
363
364 int
365 buildhints()
366 {
367 struct hints_header hdr;
368 struct hints_bucket *blist;
369 struct shlib_list *shp;
370 char *strtab;
371 int i, n, str_index = 0;
372 int strtab_sz = 0; /* Total length of strings */
373 int nhints = 0; /* Total number of hints */
374 int fd;
375 char *tmpfile;
376
377 for (shp = shlib_head; shp; shp = shp->next) {
378 strtab_sz += 1 + strlen(shp->name);
379 strtab_sz += 1 + strlen(shp->path);
380 nhints++;
381 }
382
383 /* Fill hints file header */
384 hdr.hh_magic = HH_MAGIC;
385 hdr.hh_version = LD_HINTS_VERSION_2;
386 hdr.hh_nbucket = 1 * nhints;
387 n = hdr.hh_nbucket * sizeof(struct hints_bucket);
388 hdr.hh_hashtab = sizeof(struct hints_header);
389 hdr.hh_strtab = hdr.hh_hashtab + n;
390 hdr.hh_dirlist = strtab_sz;
391 strtab_sz += 1 + strlen(dir_list);
392 hdr.hh_strtab_sz = strtab_sz;
393 hdr.hh_ehints = hdr.hh_strtab + hdr.hh_strtab_sz;
394
395 if (verbose)
396 printf("Totals: entries %d, buckets %ld, string size %d\n",
397 nhints, hdr.hh_nbucket, strtab_sz);
398
399 /* Allocate buckets and string table */
400 blist = (struct hints_bucket *)xmalloc(n);
401 memset(blist, 0, n);
402 for (i = 0; i < hdr.hh_nbucket; i++)
403 /* Empty all buckets */
404 blist[i].hi_next = -1;
405
406 strtab = (char *)xmalloc(strtab_sz);
407
408 /* Enter all */
409 for (shp = shlib_head; shp; shp = shp->next) {
410 struct hints_bucket *bp;
411
412 bp = blist +
413 (hinthash(shp->name, shp->major, shp->minor) % hdr.hh_nbucket);
414
415 if (bp->hi_pathx) {
416 int i;
417
418 for (i = 0; i < hdr.hh_nbucket; i++) {
419 if (blist[i].hi_pathx == 0)
420 break;
421 }
422 if (i == hdr.hh_nbucket) {
423 warnx("Bummer!");
424 return (-1);
425 }
426 while (bp->hi_next != -1)
427 bp = &blist[bp->hi_next];
428 bp->hi_next = i;
429 bp = blist + i;
430 }
431
432 /* Insert strings in string table */
433 bp->hi_namex = str_index;
434 strcpy(strtab + str_index, shp->name);
435 str_index += 1 + strlen(shp->name);
436
437 bp->hi_pathx = str_index;
438 strcpy(strtab + str_index, shp->path);
439 str_index += 1 + strlen(shp->path);
440
441 /* Copy versions */
442 memcpy(bp->hi_dewey, shp->dewey, sizeof(bp->hi_dewey));
443 bp->hi_ndewey = shp->ndewey;
444 }
445
446 /* Copy search directories */
447 strcpy(strtab + str_index, dir_list);
448 str_index += 1 + strlen(dir_list);
449
450 /* Sanity check */
451 if (str_index != strtab_sz) {
452 errx(1, "str_index(%d) != strtab_sz(%d)", str_index, strtab_sz);
453 }
454
455 tmpfile = concat(_PATH_LD_HINTS, ".XXXXXX", "");
456 if ((fd = mkstemp(tmpfile)) == -1) {
457 warn("%s", tmpfile);
458 return (-1);
459 }
460
461 if (write(fd, &hdr, sizeof(struct hints_header)) !=
462 sizeof(struct hints_header)) {
463 warn("%s", _PATH_LD_HINTS);
464 return (-1);
465 }
466 if (write(fd, blist, hdr.hh_nbucket * sizeof(struct hints_bucket)) !=
467 hdr.hh_nbucket * sizeof(struct hints_bucket)) {
468 warn("%s", _PATH_LD_HINTS);
469 return (-1);
470 }
471 if (write(fd, strtab, strtab_sz) != strtab_sz) {
472 warn("%s", _PATH_LD_HINTS);
473 return (-1);
474 }
475 if (fchmod(fd, 0444) == -1) {
476 warn("%s", _PATH_LD_HINTS);
477 return (-1);
478 }
479 if (close(fd) != 0) {
480 warn("%s", _PATH_LD_HINTS);
481 return (-1);
482 }
483
484 /* Install it */
485 if (unlink(_PATH_LD_HINTS) != 0 && errno != ENOENT) {
486 warn("%s", _PATH_LD_HINTS);
487 return (-1);
488 }
489
490 if (rename(tmpfile, _PATH_LD_HINTS) != 0) {
491 warn("%s", _PATH_LD_HINTS);
492 return (-1);
493 }
494
495 return (0);
496 }
497
498 static int
499 readhints()
500 {
501 int fd;
502 void *addr = (void *) -1;
503 size_t msize;
504 struct hints_header *hdr;
505 struct hints_bucket *blist;
506 char *strtab;
507 struct shlib_list *shp;
508 int i;
509 struct stat st;
510 int error = 0;
511
512 if ((fd = open(_PATH_LD_HINTS, O_RDONLY, 0)) == -1) {
513 warn("%s", _PATH_LD_HINTS);
514 goto cleanup;
515 }
516
517 if (fstat(fd, &st) == -1) {
518 warn("%s", _PATH_LD_HINTS);
519 goto cleanup;
520 }
521
522 msize = (size_t)st.st_size;
523 if (msize < sizeof(*hdr)) {
524 warnx("%s: File too short", _PATH_LD_HINTS);
525 goto cleanup;
526 }
527
528 addr = mmap(0, msize, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
529
530 if (addr == (void *)-1) {
531 warn("%s", _PATH_LD_HINTS);
532 goto cleanup;
533 }
534
535 hdr = (struct hints_header *)addr;
536 if (HH_BADMAG(*hdr)) {
537 warnx("%s: Bad magic: %lo",
538 _PATH_LD_HINTS, hdr->hh_magic);
539 goto cleanup;
540 }
541
542 if (hdr->hh_version != LD_HINTS_VERSION_2) {
543 warnx("Unsupported version: %ld", hdr->hh_version);
544 goto cleanup;
545 }
546
547
548 blist = (struct hints_bucket *)((char *)addr + hdr->hh_hashtab);
549 strtab = ((char *)addr + hdr->hh_strtab);
550
551 for (i = 0; i < hdr->hh_nbucket; i++) {
552 struct hints_bucket *bp = &blist[i];
553
554 /* Sanity check */
555 if (bp->hi_namex >= hdr->hh_strtab_sz) {
556 warnx("Bad name index: %#x", bp->hi_namex);
557 goto cleanup;
558 }
559 if (bp->hi_pathx >= hdr->hh_strtab_sz) {
560 warnx("Bad path index: %#x", bp->hi_pathx);
561 goto cleanup;
562 }
563
564 /* Allocate new list element */
565 shp = (struct shlib_list *)xmalloc(sizeof *shp);
566 shp->name = strdup(strtab + bp->hi_namex);
567 shp->path = strdup(strtab + bp->hi_pathx);
568 memcpy(shp->dewey, bp->hi_dewey, sizeof(shp->dewey));
569 shp->ndewey = bp->hi_ndewey;
570 shp->next = NULL;
571
572 *shlib_tail = shp;
573 shlib_tail = &shp->next;
574 }
575 dir_list = strdup(strtab + hdr->hh_dirlist);
576 goto done;
577 cleanup:
578 error = 1;
579 done:
580 if (fd != -1)
581 close(fd);
582 if (addr != (void *)-1)
583 munmap(addr, msize);
584 return error;
585
586 }
587
588 static void
589 listhints()
590 {
591 struct shlib_list *shp;
592 int i;
593
594 printf("%s:\n", _PATH_LD_HINTS);
595 printf("\tsearch directories: %s\n", dir_list);
596
597 for (i = 0, shp = shlib_head; shp; i++, shp = shp->next)
598 printf("\t%d:-l%s.%d.%d => %s\n",
599 i, shp->name, shp->major, shp->minor, shp->path);
600
601 return;
602 }
603