xref: /minix/libexec/ld.elf_so/paths.c (revision e39e890e)
1 /*	$NetBSD: paths.c,v 1.41 2013/05/06 08:02:20 skrll Exp $	 */
2 
3 /*
4  * Copyright 1996 Matt Thomas <matt@3am-software.com>
5  * Copyright 2002 Charles M. Hannum <root@ihack.net>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. 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 #include <sys/cdefs.h>
32 #ifndef lint
33 __RCSID("$NetBSD: paths.c,v 1.41 2013/05/06 08:02:20 skrll Exp $");
34 #endif /* not lint */
35 
36 #include <err.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <sys/types.h>
45 #include <sys/param.h>
46 #include <sys/sysctl.h>
47 #include <sys/mman.h>
48 #include <sys/stat.h>
49 #include <sys/gmon.h>
50 #include <sys/socket.h>
51 #include <sys/mount.h>
52 #if !defined(__minix)
53 #include <sys/mbuf.h>
54 #endif /* !defined(__minix) */
55 #include <sys/resource.h>
56 #include <machine/cpu.h>
57 
58 #include "debug.h"
59 #include "rtld.h"
60 
61 static Search_Path *_rtld_find_path(Search_Path *, const char *, size_t);
62 static Search_Path **_rtld_append_path(Search_Path **, Search_Path **,
63     const char *, const char *, const char *);
64 static void _rtld_process_mapping(Library_Xform **, const char *,
65     const char *);
66 static char *exstrdup(const char *, const char *);
67 static const char *getstr(const char **, const char *, const char *);
68 static const char *getcstr(const char **, const char *, const char *);
69 static const char *getword(const char **, const char *, const char *);
70 #if !defined(__minix)
71 static int matchstr(const char *, const char *, const char *);
72 #endif /* !defined(__minix) */
73 
74 static const char WS[] = " \t\n";
75 
76 /*
77  * Like xstrdup(), but takes end of string as a argument.
78  */
79 static char *
80 exstrdup(const char *bp, const char *ep)
81 {
82 	char *cp;
83 	size_t len = ep - bp;
84 
85 	cp = xmalloc(len + 1);
86 	memcpy(cp, bp, len);
87 	cp[len] = '\0';
88 	return (cp);
89 }
90 
91 /*
92  * Like strsep(), but takes end of string and doesn't put any NUL.  To
93  * detect empty string, compare `*p' and return value.
94  */
95 static const char *
96 getstr(const char **p, const char *ep, const char *delim)
97 {
98 	const char *cp = *p, *q, *r;
99 
100 	if (ep < cp)
101 		/* End of string */
102 		return (NULL);
103 
104 	for (q = cp; q < ep; q++)
105 		for (r = delim; *r != 0; r++)
106 			if (*r == *q)
107 				goto done;
108 
109 done:
110 	*p = q;
111 	return (cp);
112 }
113 
114 /*
115  * Like getstr() above, but delim[] is complemented.
116  */
117 static const char *
118 getcstr(const char **p, const char *ep, const char *delim)
119 {
120 	const char *cp = *p, *q, *r;
121 
122 	if (ep < cp)
123 		/* End of string */
124 		return (NULL);
125 
126 	for (q = cp; q < ep; q++)
127 		for (r = delim; *r != *q; r++)
128 			if (*r == 0)
129 				goto done;
130 
131 done:
132 	*p = q;
133 	return (cp);
134 }
135 
136 static const char *
137 getword(const char **p, const char *ep, const char *delim)
138 {
139 
140 	(void)getcstr(p, ep, delim);
141 
142 	/*
143 	 * Now, we're looking non-delim, or end of string.
144 	 */
145 
146 	return (getstr(p, ep, delim));
147 }
148 
149 #if !defined(__minix)
150 /*
151  * Match `bp' against NUL terminated string pointed by `p'.
152  */
153 static int
154 matchstr(const char *p, const char *bp, const char *ep)
155 {
156 	int c;
157 
158 	while (bp < ep)
159 		if ((c = *p++) == 0 || c != *bp++)
160 			return (0);
161 
162 	return (*p == 0);
163 }
164 #endif /* !defined(__minix) */
165 
166 static Search_Path *
167 _rtld_find_path(Search_Path *path, const char *pathstr, size_t pathlen)
168 {
169 
170 	for (; path != NULL; path = path->sp_next) {
171 		if (pathlen == path->sp_pathlen &&
172 		    memcmp(path->sp_path, pathstr, pathlen) == 0)
173 			return path;
174 	}
175 	return NULL;
176 }
177 
178 static Search_Path **
179 _rtld_append_path(Search_Path **head_p, Search_Path **path_p,
180     const char *execname, const char *bp, const char *ep)
181 {
182 	Search_Path *path;
183 	char epath[MAXPATHLEN];
184 	size_t len;
185 
186 	len = _rtld_expand_path(epath, sizeof(epath), execname, bp, ep);
187 	if (len == 0)
188 		return path_p;
189 
190 	if (_rtld_find_path(*head_p, bp, ep - bp) != NULL)
191 		return path_p;
192 
193 	path = NEW(Search_Path);
194 	path->sp_pathlen = len;
195 	path->sp_path = exstrdup(epath, epath + len);
196 	path->sp_next = (*path_p);
197 	(*path_p) = path;
198 	path_p = &path->sp_next;
199 
200 	dbg((" added path \"%s\"", path->sp_path));
201 	return path_p;
202 }
203 
204 void
205 _rtld_add_paths(const char *execname, Search_Path **path_p, const char *pathstr)
206 {
207 	Search_Path **head_p = path_p;
208 
209 	if (pathstr == NULL)
210 		return;
211 
212 	if (pathstr[0] == ':') {
213 		/*
214 		 * Leading colon means append to current path
215 		 */
216 		while ((*path_p) != NULL)
217 			path_p = &(*path_p)->sp_next;
218 		pathstr++;
219 	}
220 
221 	for (;;) {
222 		const char *bp = pathstr;
223 		const char *ep = strchr(bp, ':');
224 		if (ep == NULL)
225 			ep = &pathstr[strlen(pathstr)];
226 
227 		path_p = _rtld_append_path(head_p, path_p, execname, bp, ep);
228 
229 		if (ep[0] == '\0')
230 			break;
231 		pathstr = ep + 1;
232 	}
233 }
234 
235 /*
236  * Process library mappings of the form:
237  *	<library_name>	<machdep_variable> <value,...:library_name,...> ...
238  */
239 static void
240 _rtld_process_mapping(Library_Xform **lib_p, const char *bp, const char *ep)
241 {
242 	Library_Xform *hwptr = NULL;
243 	const char *ptr, *key, *ekey, *lib, *elib, *l;
244 	int i, j;
245 
246 	dbg((" processing mapping \"%.*s\"", (int)(ep - bp), bp));
247 
248 	if ((ptr = getword(&bp, ep, WS)) == NULL || ptr == bp)
249 		return;
250 
251 	dbg((" library \"%.*s\"", (int)(bp - ptr), ptr));
252 
253 	hwptr = xmalloc(sizeof(*hwptr));
254 	memset(hwptr, 0, sizeof(*hwptr));
255 	hwptr->name = exstrdup(ptr, bp);
256 
257 	bp++;
258 
259 	if ((ptr = getword(&bp, ep, WS)) == NULL || ptr == bp) {
260 		xwarnx("missing sysctl variable name");
261 		goto cleanup;
262 	}
263 
264 	dbg((" sysctl \"%.*s\"", (int)(bp - ptr), ptr));
265 
266 	hwptr->ctlname = exstrdup(ptr, bp);
267 
268 	for (i = 0; bp++, (ptr = getword(&bp, ep, WS)) != NULL;) {
269 		dbg((" ptr = %.*s", (int)(bp - ptr), ptr));
270 		if (ptr == bp)
271 			continue;
272 
273 		if (i == RTLD_MAX_ENTRY) {
274 no_more:
275 			xwarnx("maximum library entries exceeded `%s'",
276 			    hwptr->name);
277 			goto cleanup;
278 		}
279 		if ((key = getstr(&ptr, bp, ":")) == NULL) {
280 			xwarnx("missing sysctl variable value for `%s'",
281 			    hwptr->name);
282 			goto cleanup;
283 		}
284 		ekey = ptr++;
285 		if ((lib = getstr(&ptr, bp, ":")) == NULL) {
286 			xwarnx("missing sysctl library list for `%s'",
287 			    hwptr->name);
288 			goto cleanup;
289 		}
290 		elib = ptr;		/* No need to advance */
291 		for (j = 0; (l = getstr(&lib, elib, ",")) != NULL;
292 		    j++, lib++) {
293 			if (j == RTLD_MAX_LIBRARY) {
294 				xwarnx("maximum library entries exceeded `%s'",
295 				    hwptr->name);
296 				goto cleanup;
297 			}
298 			dbg((" library \"%.*s\"", (int)(lib - l), l));
299 			hwptr->entry[i].library[j] = exstrdup(l, lib);
300 		}
301 		if (j == 0) {
302 			xwarnx("No library map entries for `%s/%.*s'",
303 			    hwptr->name, (int)(bp - ptr), ptr);
304 			goto cleanup;
305 		}
306 		j = i;
307 		for (; (l = getstr(&key, ekey, ",")) != NULL; i++, key++) {
308 			/*
309 			 * Allow empty key (it is valid as string
310 			 * value).  Thus, we loop at least once and
311 			 * `i' is incremented.
312 			 */
313 
314 			dbg((" key \"%.*s\"", (int)(key - l), l));
315 			if (i == RTLD_MAX_ENTRY)
316 				goto no_more;
317 			if (i != j)
318 				(void)memcpy(hwptr->entry[i].library,
319 				    hwptr->entry[j].library,
320 				    sizeof(hwptr->entry[j].library));
321 			hwptr->entry[i].value = exstrdup(l, key);
322 		}
323 	}
324 
325 	if (i == 0) {
326 		xwarnx("No library entries for `%s'", hwptr->name);
327 		goto cleanup;
328 	}
329 
330 	hwptr->next = *lib_p;
331 	*lib_p = hwptr;
332 
333 	return;
334 
335 cleanup:
336 	if (hwptr->name)
337 		xfree(hwptr->name);
338 	xfree(hwptr);
339 }
340 
341 void
342 _rtld_process_hints(const char *execname, Search_Path **path_p,
343     Library_Xform **lib_p, const char *fname)
344 {
345 	int fd;
346 	char *buf, small[128];
347 	const char *b, *ep, *ptr;
348 	struct stat st;
349 	ssize_t sz;
350 	Search_Path **head_p = path_p;
351 
352 	if ((fd = open(fname, O_RDONLY)) == -1) {
353 		/* Don't complain */
354 		return;
355 	}
356 
357 	/* Try to avoid mmap/stat on the file. */
358 	buf = small;
359 	buf[0] = '\0';
360 	sz = read(fd, buf, sizeof(small));
361 	if (sz == -1) {
362 		xwarn("read: %s", fname);
363 		(void)close(fd);
364 		return;
365 	}
366 	if (sz >= (ssize_t)sizeof(small)) {
367 		if (fstat(fd, &st) == -1) {
368 			/* Complain */
369 			xwarn("fstat: %s", fname);
370 			(void)close(fd);
371 			return;
372 		}
373 
374 		sz = (ssize_t) st.st_size;
375 
376 		buf = mmap(0, sz, PROT_READ, MAP_SHARED|MAP_FILE, fd, 0);
377 		if (buf == MAP_FAILED) {
378 			xwarn("mmap: %s", fname);
379 			(void)close(fd);
380 			return;
381 		}
382 	}
383 	(void)close(fd);
384 
385 	while ((*path_p) != NULL)
386 		path_p = &(*path_p)->sp_next;
387 
388 	for (b = buf, ep = buf + sz; b < ep; b++) {
389 		(void)getcstr(&b, ep, WS);
390 		if (b == ep)
391 			break;
392 
393 		ptr = getstr(&b, ep, "\n#");
394 		if (*ptr == '/') {
395 			/*
396 			 * Since '/' != '\n' and != '#', we know ptr <
397 			 * b.  And we will stop when b[-1] == '/'.
398 			 */
399 			while (b[-1] == ' ' || b[-1] == '\t')
400 				b--;
401 			path_p = _rtld_append_path(head_p, path_p, execname,
402 			    ptr, b);
403 		} else
404 			_rtld_process_mapping(lib_p, ptr, b);
405 
406 		/*
407 		 * b points one of ' ', \t, \n, # or equal to ep.  So,
408 		 * make sure we are at newline or end of string.
409 		 */
410 		(void)getstr(&b, ep, "\n");
411 	}
412 
413 	if (buf != small)
414 		(void)munmap(buf, sz);
415 }
416 
417 #if !defined(__minix)
418 /* Basic name -> sysctl MIB translation */
419 int
420 _rtld_sysctl(const char *name, void *oldp, size_t *oldlen)
421 {
422 	const char *node, *ep;
423 	struct sysctlnode query, *result, *newresult;
424 	int mib[CTL_MAXNAME], r;
425 	size_t res_size, n, i;
426 	u_int miblen = 0;
427 
428 	/* Start with 16 entries, will grow it up as needed. */
429 	res_size = 16 * sizeof(struct sysctlnode);
430 	result = xmalloc(res_size);
431 	if (result == NULL)
432 		return (-1);
433 
434 	ep = name + strlen(name);
435 	do {
436 		i = ~0ul;
437 		while (*name == '/' || *name == '.')
438 			name++;
439 		if (name >= ep)
440 			break;
441 
442 		mib[miblen] = CTL_QUERY;
443 		memset(&query, 0, sizeof(query));
444 		query.sysctl_flags = SYSCTL_VERSION;
445 
446 		n = res_size;
447 		if (sysctl(mib, miblen + 1, result, &n, &query,
448 		    sizeof(query)) == -1) {
449 			if (errno != ENOMEM)
450 				goto bad;
451 			/* Grow up result */
452 			res_size = n;
453 			newresult = xrealloc(result, res_size);
454 			if (newresult == NULL)
455 				goto bad;
456 			result = newresult;
457 			if (sysctl(mib, miblen + 1, result, &n, &query,
458 			    sizeof(query)) == -1)
459 				goto bad;
460 		}
461 		n /= sizeof(struct sysctlnode);
462 
463 		node = getstr(&name, ep, "./");
464 
465 		for (i = 0; i < n; i++)
466 			if (matchstr(result[i].sysctl_name, node, name)) {
467 				mib[miblen] = result[i].sysctl_num;
468 				miblen++;
469 				break;
470 			}
471 	} while (name < ep && miblen <= CTL_MAXNAME);
472 
473 	if (name < ep || i == ~0ul)
474 		goto bad;
475 	r = SYSCTL_TYPE(result[i].sysctl_flags);
476 
477 	xfree(result);
478 	if (sysctl(mib, miblen, oldp, oldlen, NULL, 0) == -1)
479 		return (-1);
480 	return r;
481 
482 bad:
483 	xfree(result);
484 	return (-1);
485 }
486 #endif /* !defined(__minix) */
487