Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sbin / ldconfig / ldconfig.c
1 /*
2  * Copyright (c) 1993,1995 Paul Kranenburg
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Paul Kranenburg.
16  * 4. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #ifndef lint
32 static const char rcsid[] =
33   "$FreeBSD: src/sbin/ldconfig/ldconfig.c,v 1.31.2.3 2001/07/11 23:59:10 obrien Exp $";
34 #endif /* not lint */
35
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/mman.h>
40 #include <a.out.h>
41 #include <ctype.h>
42 #include <dirent.h>
43 #include <elf-hints.h>
44 #include <err.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <link.h>
48 #include <objformat.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53
54 #include "ldconfig.h"
55 #include "shlib.h"
56 #include "support.h"
57
58 #if DEBUG
59 /* test */
60 #undef _PATH_LD_HINTS
61 #define _PATH_LD_HINTS          "./ld.so.hints"
62 #undef _PATH_ELF_HINTS
63 #define _PATH_ELF_HINTS         "./ld-elf.so.hints"
64 #endif
65
66 #undef major
67 #undef minor
68
69 static int                      verbose;
70 static int                      nostd;
71 static int                      justread;
72 static int                      merge;
73 static int                      rescan;
74 static char                     *hints_file;
75
76 struct shlib_list {
77         /* Internal list of shared libraries found */
78         char                    *name;
79         char                    *path;
80         int                     dewey[MAXDEWEY];
81         int                     ndewey;
82 #define major dewey[0]
83 #define minor dewey[1]
84         struct shlib_list       *next;
85 };
86
87 static struct shlib_list        *shlib_head = NULL, **shlib_tail = &shlib_head;
88 static char                     *dir_list;
89
90 static int              buildhints __P((void));
91 static int              dodir __P((char *, int));
92 int                     dofile __P((char *, int));
93 static void             enter __P((char *, char *, char *, int *, int));
94 static void             listhints __P((void));
95 static int              readhints __P((void));
96 static void             usage __P((void));
97
98 int
99 main(argc, argv)
100 int     argc;
101 char    *argv[];
102 {
103         int             i, c;
104         int             rval = 0;
105         char            objformat[32];
106         int             is_aout;
107
108         if (getobjformat(objformat, sizeof objformat, &argc, argv) == -1)
109                 errx(1, "getobjformat failed: name too long");
110         if (strcmp(objformat, "aout") == 0)
111                 is_aout = 1;
112         else if (strcmp(objformat, "elf") == 0)
113                 is_aout = 0;
114         else
115                 errx(1, "unknown object format \"%s\"", objformat);
116
117         hints_file = is_aout ? _PATH_LD_HINTS : _PATH_ELF_HINTS;
118         if (argc == 1)
119                 rescan = 1;
120         else while((c = getopt(argc, argv, "Rf:imrsv")) != -1) {
121                 switch (c) {
122                 case 'R':
123                         rescan = 1;
124                         break;
125                 case 'f':
126                         hints_file = optarg;
127                         break;
128                 case 'i':
129                         insecure = 1;
130                         break;
131                 case 'm':
132                         merge = 1;
133                         break;
134                 case 'r':
135                         justread = 1;
136                         break;
137                 case 's':
138                         nostd = 1;
139                         break;
140                 case 'v':
141                         verbose = 1;
142                         break;
143                 default:
144                         usage();
145                         break;
146                 }
147         }
148
149         if (!is_aout) {
150                 if (justread)
151                         list_elf_hints(hints_file);
152                 else
153                         update_elf_hints(hints_file, argc - optind,
154                             argv + optind, merge || rescan);
155                 return 0;
156         }
157
158         /* Here begins the aout libs processing */
159         dir_list = strdup("");
160
161         if (justread || merge || rescan) {
162                 if ((rval = readhints()) != 0)
163                         return rval;
164         }
165
166         if (!nostd && !merge && !rescan)
167                 std_search_path();
168
169         /* Add any directories/files from the command line */
170         if (!justread) {
171                 for (i = optind; i < argc; i++) {
172                         struct stat stbuf;
173
174                         if (stat(argv[i], &stbuf) == -1) {
175                                 warn("%s", argv[i]);
176                                 rval = -1;
177                         } else if (strcmp(argv[i], "/usr/lib") == 0) {
178                                 warnx("WARNING! '%s' can not be used", argv[i]);
179                                 rval = -1;
180                         } else {
181                                 /*
182                                  * See if this is a directory-containing
183                                  * file instead of a directory
184                                  */
185                                 if (S_ISREG(stbuf.st_mode))
186                                         rval |= dofile(argv[i], 0);
187                                 else
188                                         add_search_path(argv[i]);
189                         }
190                 }
191         }
192
193         for (i = 0; i < n_search_dirs; i++) {
194                 char *cp = concat(dir_list, *dir_list?":":"", search_dirs[i]);
195                 free(dir_list);
196                 dir_list = cp;
197         }
198
199         if (justread) {
200                 listhints();
201                 return 0;
202         }
203
204         for (i = 0; i < n_search_dirs; i++)
205                 rval |= dodir(search_dirs[i], 1);
206
207         rval |= buildhints();
208
209         return rval;
210 }
211
212 static void
213 usage()
214 {
215         fprintf(stderr,
216         "usage: ldconfig [-aout | -elf] [-Rimrsv] [-f hints_file] [dir | file ...]\n");
217         exit(1);
218 }
219         
220 int
221 dofile(fname, silent)
222 char    *fname;
223 int     silent;
224 {
225         FILE *hfp;
226         char buf[MAXPATHLEN];
227         int rval = 0;
228         char *cp, *sp;
229
230         if ((hfp = fopen(fname, "r")) == NULL) {
231                 warn("%s", fname);
232                 return -1;
233         }
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
259 int
260 dodir(dir, silent)
261 char    *dir;
262 int     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                 register int n;
278                 register 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
318 static void
319 enter(dir, file, name, dewey, ndewey)
320 char    *dir, *file, *name;
321 int     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
368 int
369 hinthash(cp, vmajor)
370 char    *cp;
371 int     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
383 int
384 buildhints()
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
515 static int
516 readhints()
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
606 static void
607 listhints()
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 }