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