20 #ifndef _PATH_LIBMAP_CONF
21 #define _PATH_LIBMAP_CONF "/etc/libmap.conf"
24 TAILQ_HEAD(lm_list
, lm
);
28 TAILQ_ENTRY(lm
) lm_link
;
31 TAILQ_HEAD(lmp_list
, lmp
) lmp_head
= TAILQ_HEAD_INITIALIZER(lmp_head
);
34 enum { T_EXACT
=0, T_BASENAME
, T_DIRECTORY
} type
;
36 TAILQ_ENTRY(lmp
) lmp_link
;
39 static TAILQ_HEAD(lmc_list
, lmc
) lmc_head
= TAILQ_HEAD_INITIALIZER(lmc_head
);
42 TAILQ_ENTRY(lmc
) next
;
47 static void lmc_parse(char *, size_t);
48 static void lmc_parse_file(char *);
49 static void lmc_parse_dir(char *);
50 static void lm_add(const char *, const char *, const char *);
51 static void lm_free(struct lm_list
*);
52 static char *lml_find(struct lm_list
*, const char *);
53 static struct lm_list
*lmp_find(const char *);
54 static struct lm_list
*lmp_init(char *);
55 static const char *quickbasename(const char *);
57 #define iseol(c) (((c) == '#') || ((c) == '\0') || \
58 ((c) == '\n') || ((c) == '\r'))
61 * Do not use ctype.h macros, which rely on working TLS. It is
62 * too early to have thread-local variables functional.
64 #define rtld_isspace(c) ((c) == ' ' || (c) == '\t')
67 lm_init(char *libmap_override
)
71 dbg("lm_init(\"%s\")", libmap_override
);
72 TAILQ_INIT(&lmp_head
);
74 lmc_parse_file(_PATH_LIBMAP_CONF
);
76 if (libmap_override
) {
78 * Do some character replacement to make $LDLIBMAP look
79 * like a text file, then parse it.
81 libmap_override
= xstrdup(libmap_override
);
82 for (p
= libmap_override
; *p
; p
++) {
92 lmc_parse(libmap_override
, p
- libmap_override
);
93 free(libmap_override
);
96 return (lm_count
== 0);
100 lmc_parse_file(char *path
)
108 rpath
= realpath(path
, NULL
);
112 TAILQ_FOREACH(p
, &lmc_head
, next
) {
113 if (strcmp(p
->path
, rpath
) == 0) {
119 fd
= open(rpath
, O_RDONLY
| O_CLOEXEC
);
121 dbg("lm_parse_file: open(\"%s\") failed, %s", rpath
,
122 rtld_strerror(errno
));
126 if (fstat(fd
, &st
) == -1) {
128 dbg("lm_parse_file: fstat(\"%s\") failed, %s", rpath
,
129 rtld_strerror(errno
));
133 lm_map
= mmap(NULL
, st
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
134 if (lm_map
== (const char *)MAP_FAILED
) {
136 dbg("lm_parse_file: mmap(\"%s\") failed, %s", rpath
,
137 rtld_strerror(errno
));
142 p
= xmalloc(sizeof(struct lmc
));
144 TAILQ_INSERT_HEAD(&lmc_head
, p
, next
);
145 lmc_parse(lm_map
, st
.st_size
);
146 munmap(lm_map
, st
.st_size
);
150 lmc_parse_dir(char *idir
)
155 char conffile
[MAXPATHLEN
];
159 rpath
= realpath(idir
, NULL
);
163 TAILQ_FOREACH(p
, &lmc_head
, next
) {
164 if (strcmp(p
->path
, rpath
) == 0) {
175 p
= xmalloc(sizeof(struct lmc
));
177 TAILQ_INSERT_HEAD(&lmc_head
, p
, next
);
179 while ((dp
= readdir(d
)) != NULL
) {
182 if (dp
->d_type
!= DT_REG
)
184 ext
= strrchr(dp
->d_name
, '.');
187 if (strcmp(ext
, ".conf") != 0)
189 if (strlcpy(conffile
, idir
, MAXPATHLEN
) >= MAXPATHLEN
)
190 continue; /* too long */
191 if (strlcat(conffile
, "/", MAXPATHLEN
) >= MAXPATHLEN
)
192 continue; /* too long */
193 if (strlcat(conffile
, dp
->d_name
, MAXPATHLEN
) >= MAXPATHLEN
)
194 continue; /* too long */
195 lmc_parse_file(conffile
);
201 lmc_parse(char *lm_p
, size_t lm_len
)
203 char *cp
, *f
, *t
, *c
, *p
;
204 char prog
[MAXPATHLEN
];
205 /* allow includedir + full length path */
206 char line
[MAXPATHLEN
+ 13];
212 while (cnt
< lm_len
) {
214 while (cnt
< lm_len
&& lm_p
[cnt
] != '\n' &&
215 i
< sizeof(line
) - 1) {
221 while (cnt
< lm_len
&& lm_p
[cnt
] != '\n')
229 /* Skip over leading space */
230 while (rtld_isspace(*cp
)) cp
++;
232 /* Found a comment or EOL */
233 if (iseol(*cp
)) continue;
235 /* Found a constraint selector */
239 /* Skip leading space */
240 while (rtld_isspace(*cp
)) cp
++;
242 /* Found comment, EOL or end of selector */
243 if (iseol(*cp
) || *cp
== ']')
247 /* Skip to end of word */
248 while (!rtld_isspace(*cp
) && !iseol(*cp
) && *cp
!= ']')
251 /* Skip and zero out trailing space */
252 while (rtld_isspace(*cp
)) *cp
++ = '\0';
254 /* Check if there is a closing brace */
255 if (*cp
!= ']') continue;
257 /* Terminate string if there was no trailing space */
261 * There should be nothing except whitespace or comment
262 from this point to the end of the line.
264 while(rtld_isspace(*cp
)) cp
++;
265 if (!iseol(*cp
)) continue;
267 if (strlcpy(prog
, c
, sizeof prog
) >= sizeof prog
)
273 /* Parse the 'from' candidate. */
275 while (!rtld_isspace(*cp
) && !iseol(*cp
)) cp
++;
277 /* Skip and zero out the trailing whitespace */
278 while (rtld_isspace(*cp
)) *cp
++ = '\0';
280 /* Found a comment or EOL */
281 if (iseol(*cp
)) continue;
283 /* Parse 'to' mapping */
285 while (!rtld_isspace(*cp
) && !iseol(*cp
)) cp
++;
287 /* Skip and zero out the trailing whitespace */
288 while (rtld_isspace(*cp
)) *cp
++ = '\0';
290 /* Should be no extra tokens at this point */
291 if (!iseol(*cp
)) continue;
294 if (strcmp(f
, "includedir") == 0)
296 else if (strcmp(f
, "include") == 0)
304 lm_free (struct lm_list
*lml
)
308 dbg("%s(%p)", __func__
, lml
);
310 while (!TAILQ_EMPTY(lml
)) {
311 lm
= TAILQ_FIRST(lml
);
312 TAILQ_REMOVE(lml
, lm
, lm_link
);
326 dbg("%s()", __func__
);
328 while (!TAILQ_EMPTY(&lmc_head
)) {
329 p
= TAILQ_FIRST(&lmc_head
);
330 TAILQ_REMOVE(&lmc_head
, p
, next
);
335 while (!TAILQ_EMPTY(&lmp_head
)) {
336 lmp
= TAILQ_FIRST(&lmp_head
);
337 TAILQ_REMOVE(&lmp_head
, lmp
, lmp_link
);
346 lm_add (const char *p
, const char *f
, const char *t
)
354 dbg("%s(\"%s\", \"%s\", \"%s\")", __func__
, p
, f
, t
);
356 if ((lml
= lmp_find(p
)) == NULL
)
357 lml
= lmp_init(xstrdup(p
));
359 lm
= xmalloc(sizeof(struct lm
));
362 TAILQ_INSERT_HEAD(lml
, lm
, lm_link
);
367 lm_find (const char *p
, const char *f
)
372 dbg("%s(\"%s\", \"%s\")", __func__
, p
, f
);
374 if (p
!= NULL
&& (lml
= lmp_find(p
)) != NULL
) {
375 t
= lml_find(lml
, f
);
378 * Add a global mapping if we have
379 * a successful constrained match.
385 lml
= lmp_find("$DEFAULT$");
387 return (lml_find(lml
, f
));
392 /* Given a libmap translation list and a library name, return the
393 replacement library, or NULL */
395 lm_findn (const char *p
, const char *f
, const int n
)
397 char pathbuf
[64], *s
, *t
;
399 if (n
< sizeof(pathbuf
) - 1)
412 lml_find (struct lm_list
*lmh
, const char *f
)
416 dbg("%s(%p, \"%s\")", __func__
, lmh
, f
);
418 TAILQ_FOREACH(lm
, lmh
, lm_link
)
419 if (strcmp(f
, lm
->f
) == 0)
424 /* Given an executable name, return a pointer to the translation list or
425 NULL if no matches */
426 static struct lm_list
*
427 lmp_find (const char *n
)
431 dbg("%s(\"%s\")", __func__
, n
);
433 TAILQ_FOREACH(lmp
, &lmp_head
, lmp_link
)
434 if ((lmp
->type
== T_EXACT
&& strcmp(n
, lmp
->p
) == 0) ||
435 (lmp
->type
== T_DIRECTORY
&& strncmp(n
, lmp
->p
, strlen(lmp
->p
)) == 0) ||
436 (lmp
->type
== T_BASENAME
&& strcmp(quickbasename(n
), lmp
->p
) == 0))
441 static struct lm_list
*
446 dbg("%s(\"%s\")", __func__
, n
);
448 lmp
= xmalloc(sizeof(struct lmp
));
450 if (n
[strlen(n
)-1] == '/')
451 lmp
->type
= T_DIRECTORY
;
452 else if (strchr(n
,'/') == NULL
)
453 lmp
->type
= T_BASENAME
;
456 TAILQ_INIT(&lmp
->lml
);
457 TAILQ_INSERT_HEAD(&lmp_head
, lmp
, lmp_link
);
462 /* libc basename is overkill. Return a pointer to the character after the
463 last /, or the original string if there are no slashes. */
465 quickbasename (const char *path
)
467 const char *p
= path
;
468 for (; *path
; path
++) {