xref: /dragonfly/libexec/rtld-elf/libmap.c (revision 2020c8fe)
1 /*
2  * $FreeBSD$
3  */
4 
5 #include <stdio.h>
6 #include <ctype.h>
7 #include <string.h>
8 #include <stdlib.h>
9 #include <sys/queue.h>
10 #include <sys/param.h>
11 
12 #include "debug.h"
13 #include "rtld.h"
14 #include "libmap.h"
15 
16 #ifndef _PATH_LIBMAP_CONF
17 #define	_PATH_LIBMAP_CONF	"/etc/libmap.conf"
18 #endif
19 
20 TAILQ_HEAD(lm_list, lm);
21 struct lm {
22 	char *f;
23 	char *t;
24 
25 	TAILQ_ENTRY(lm)	lm_link;
26 };
27 
28 TAILQ_HEAD(lmp_list, lmp) lmp_head = TAILQ_HEAD_INITIALIZER(lmp_head);
29 struct lmp {
30 	char *p;
31 	enum { T_EXACT=0, T_BASENAME, T_DIRECTORY } type;
32 	struct lm_list lml;
33 	TAILQ_ENTRY(lmp) lmp_link;
34 };
35 
36 static int	lm_count;
37 
38 static void		lmc_parse	(FILE *);
39 static void		lm_add		(const char *, const char *, const char *);
40 static void		lm_free		(struct lm_list *);
41 static char *		lml_find	(struct lm_list *, const char *);
42 static struct lm_list *	lmp_find	(const char *);
43 static struct lm_list *	lmp_init	(char *);
44 static const char * quickbasename	(const char *);
45 static int	readstrfn	(void * cookie, char *buf, int len);
46 static int	closestrfn	(void * cookie);
47 
48 #define	iseol(c)	(((c) == '#') || ((c) == '\0') || \
49 			 ((c) == '\n') || ((c) == '\r'))
50 
51 int
52 lm_init (char *libmap_override)
53 {
54 	FILE	*fp;
55 
56 	dbg("%s(\"%s\")", __func__, libmap_override);
57 
58 	TAILQ_INIT(&lmp_head);
59 
60 	fp = fopen(_PATH_LIBMAP_CONF, "r");
61 	if (fp) {
62 		lmc_parse(fp);
63 		fclose(fp);
64 	}
65 
66 	if (libmap_override) {
67 		char	*p;
68 		/* do some character replacement to make $LIBMAP look like a
69 		   text file, then "open" it with funopen */
70 		libmap_override = xstrdup(libmap_override);
71 
72 		for (p = libmap_override; *p; p++) {
73 			switch (*p) {
74 				case '=':
75 					*p = ' '; break;
76 				case ',':
77 					*p = '\n'; break;
78 			}
79 		}
80 		fp = funopen(libmap_override, readstrfn, NULL, NULL, closestrfn);
81 		if (fp) {
82 			lmc_parse(fp);
83 			fclose(fp);
84 		}
85 	}
86 
87 	return (lm_count == 0);
88 }
89 
90 static void
91 lmc_parse (FILE *fp)
92 {
93 	char	*cp;
94 	char	*f, *t, *c, *p;
95 	char	prog[MAXPATHLEN];
96 	char	line[MAXPATHLEN + 2];
97 
98 	dbg("%s(%p)", __func__, fp);
99 
100 	p = NULL;
101 	while ((cp = fgets(line, MAXPATHLEN + 1, fp)) != NULL) {
102 		t = f = c = NULL;
103 
104 		/* Skip over leading space */
105 		while (isspace(*cp)) cp++;
106 
107 		/* Found a comment or EOL */
108 		if (iseol(*cp)) continue;
109 
110 		/* Found a constraint selector */
111 		if (*cp == '[') {
112 			cp++;
113 
114 			/* Skip leading space */
115 			while (isspace(*cp)) cp++;
116 
117 			/* Found comment, EOL or end of selector */
118 			if  (iseol(*cp) || *cp == ']')
119 				continue;
120 
121 			c = cp++;
122 			/* Skip to end of word */
123 			while (!isspace(*cp) && !iseol(*cp) && *cp != ']')
124 				cp++;
125 
126 			/* Skip and zero out trailing space */
127 			while (isspace(*cp)) *cp++ = '\0';
128 
129 			/* Check if there is a closing brace */
130 			if (*cp != ']') continue;
131 
132 			/* Terminate string if there was no trailing space */
133 			*cp++ = '\0';
134 
135 			/*
136 			 * There should be nothing except whitespace or comment
137 			  from this point to the end of the line.
138 			 */
139 			while(isspace(*cp)) cp++;
140 			if (!iseol(*cp)) continue;
141 
142 			strcpy(prog, c);
143 			p = prog;
144 			continue;
145 		}
146 
147 		/* Parse the 'from' candidate. */
148 		f = cp++;
149 		while (!isspace(*cp) && !iseol(*cp)) cp++;
150 
151 		/* Skip and zero out the trailing whitespace */
152 		while (isspace(*cp)) *cp++ = '\0';
153 
154 		/* Found a comment or EOL */
155 		if (iseol(*cp)) continue;
156 
157 		/* Parse 'to' mapping */
158 		t = cp++;
159 		while (!isspace(*cp) && !iseol(*cp)) cp++;
160 
161 		/* Skip and zero out the trailing whitespace */
162 		while (isspace(*cp)) *cp++ = '\0';
163 
164 		/* Should be no extra tokens at this point */
165 		if (!iseol(*cp)) continue;
166 
167 		*cp = '\0';
168 		lm_add(p, f, t);
169 	}
170 }
171 
172 static void
173 lm_free (struct lm_list *lml)
174 {
175 	struct lm *lm;
176 
177 	dbg("%s(%p)", __func__, lml);
178 
179 	while (!TAILQ_EMPTY(lml)) {
180 		lm = TAILQ_FIRST(lml);
181 		TAILQ_REMOVE(lml, lm, lm_link);
182 		free(lm->f);
183 		free(lm->t);
184 		free(lm);
185 	}
186 	return;
187 }
188 
189 void
190 lm_fini (void)
191 {
192 	struct lmp *lmp;
193 
194 	dbg("%s()", __func__);
195 
196 	while (!TAILQ_EMPTY(&lmp_head)) {
197 		lmp = TAILQ_FIRST(&lmp_head);
198 		TAILQ_REMOVE(&lmp_head, lmp, lmp_link);
199 		free(lmp->p);
200 		lm_free(&lmp->lml);
201 		free(lmp);
202 	}
203 	return;
204 }
205 
206 static void
207 lm_add (const char *p, const char *f, const char *t)
208 {
209 	struct lm_list *lml;
210 	struct lm *lm;
211 
212 	if (p == NULL)
213 		p = "$DEFAULT$";
214 
215 	dbg("%s(\"%s\", \"%s\", \"%s\")", __func__, p, f, t);
216 
217 	if ((lml = lmp_find(p)) == NULL)
218 		lml = lmp_init(xstrdup(p));
219 
220 	lm = xmalloc(sizeof(struct lm));
221 	lm->f = xstrdup(f);
222 	lm->t = xstrdup(t);
223 	TAILQ_INSERT_HEAD(lml, lm, lm_link);
224 	lm_count++;
225 }
226 
227 char *
228 lm_find (const char *p, const char *f)
229 {
230 	struct lm_list *lml;
231 	char *t;
232 
233 	dbg("%s(\"%s\", \"%s\")", __func__, p, f);
234 
235 	if (p != NULL && (lml = lmp_find(p)) != NULL) {
236 		t = lml_find(lml, f);
237 		if (t != NULL) {
238 			/*
239 			 * Add a global mapping if we have
240 			 * a successful constrained match.
241 			 */
242 			lm_add(NULL, f, t);
243 			return (t);
244 		}
245 	}
246 	lml = lmp_find("$DEFAULT$");
247 	if (lml != NULL)
248 		return (lml_find(lml, f));
249 	else
250 		return (NULL);
251 }
252 
253 static char *
254 lml_find (struct lm_list *lmh, const char *f)
255 {
256 	struct lm *lm;
257 
258 	dbg("%s(%p, \"%s\")", __func__, lmh, f);
259 
260 	TAILQ_FOREACH(lm, lmh, lm_link)
261 		if (strcmp(f, lm->f) == 0)
262 			return (lm->t);
263 	return (NULL);
264 }
265 
266 /* Given an executable name, return a pointer to the translation list or
267    NULL if no matches */
268 static struct lm_list *
269 lmp_find (const char *n)
270 {
271 	struct lmp *lmp;
272 
273 	dbg("%s(\"%s\")", __func__, n);
274 
275 	TAILQ_FOREACH(lmp, &lmp_head, lmp_link)
276 		if ((lmp->type == T_EXACT && strcmp(n, lmp->p) == 0) ||
277 		    (lmp->type == T_DIRECTORY && strncmp(n, lmp->p, strlen(lmp->p)) == 0) ||
278 		    (lmp->type == T_BASENAME && strcmp(quickbasename(n), lmp->p) == 0))
279 			return (&lmp->lml);
280 	return (NULL);
281 }
282 
283 static struct lm_list *
284 lmp_init (char *n)
285 {
286 	struct lmp *lmp;
287 
288 	dbg("%s(\"%s\")", __func__, n);
289 
290 	lmp = xmalloc(sizeof(struct lmp));
291 	lmp->p = n;
292 	if (n[strlen(n)-1] == '/')
293 		lmp->type = T_DIRECTORY;
294 	else if (strchr(n,'/') == NULL)
295 		lmp->type = T_BASENAME;
296 	else
297 		lmp->type = T_EXACT;
298 	TAILQ_INIT(&lmp->lml);
299 	TAILQ_INSERT_HEAD(&lmp_head, lmp, lmp_link);
300 
301 	return (&lmp->lml);
302 }
303 
304 /* libc basename is overkill.  Return a pointer to the character after the
305    last /, or the original string if there are no slashes. */
306 static const char *
307 quickbasename (const char *path)
308 {
309 	const char *p = path;
310 	for (; *path; path++) {
311 		if (*path == '/')
312 			p = path+1;
313 	}
314 	return (p);
315 }
316 
317 static int
318 readstrfn(void * cookie, char *buf, int len)
319 {
320 	static char	*current;
321 	static int	left;
322 	int 	copied;
323 
324 	copied = 0;
325 	if (!current) {
326 		current = cookie;
327 		left = strlen(cookie);
328 	}
329 	while (*current && left && len) {
330 		*buf++ = *current++;
331 		left--;
332 		len--;
333 		copied++;
334 	}
335 	return copied;
336 }
337 
338 static int
339 closestrfn(void * cookie)
340 {
341 	free(cookie);
342 	return 0;
343 }
344