1/*	$NetBSD: paths.c,v 1.39 2008/06/05 00:03:20 ad 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.39 2008/06/05 00:03:20 ad 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
59static Search_Path *_rtld_find_path(Search_Path *, const char *, size_t);
60static Search_Path **_rtld_append_path(Search_Path **, Search_Path **,
61    const char *, const char *, const char *);
62static void _rtld_process_mapping(Library_Xform **, const char *,
63    const char *);
64static char *exstrdup(const char *, const char *);
65static const char *getstr(const char **, const char *, const char *);
66static const char *getcstr(const char **, const char *, const char *);
67static const char *getword(const char **, const char *, const char *);
68static int matchstr(const char *, const char *, const char *);
69
70static const char WS[] = " \t\n";
71
72/*
73 * Like xstrdup(), but takes end of string as a argument.
74 */
75static char *
76exstrdup(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 */
91static const char *
92getstr(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
105done:
106	*p = q;
107	return (cp);
108}
109
110/*
111 * Like getstr() above, but delim[] is complemented.
112 */
113static const char *
114getcstr(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
127done:
128	*p = q;
129	return (cp);
130}
131
132static const char *
133getword(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 */
148static int
149matchstr(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
160static 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
172static Search_Path **
173_rtld_append_path(Search_Path **head_p, Search_Path **path_p,
174    const char *execname, const char *bp, const char *ep)
175{
176	Search_Path *path;
177	char epath[MAXPATHLEN];
178	size_t len;
179
180	len = _rtld_expand_path(epath, sizeof(epath), execname, bp, ep);
181	if (len == 0)
182		return path_p;
183
184	if (_rtld_find_path(*head_p, bp, ep - bp) != NULL)
185		return path_p;
186
187	path = NEW(Search_Path);
188	path->sp_pathlen = len;
189	path->sp_path = exstrdup(epath, epath + len);
190	path->sp_next = (*path_p);
191	(*path_p) = path;
192	path_p = &path->sp_next;
193
194	dbg((" added path \"%s\"", path->sp_path));
195	return path_p;
196}
197
198void
199_rtld_add_paths(const char *execname, Search_Path **path_p, const char *pathstr)
200{
201	Search_Path **head_p = path_p;
202
203	if (pathstr == NULL)
204		return;
205
206	if (pathstr[0] == ':') {
207		/*
208		 * Leading colon means append to current path
209		 */
210		while ((*path_p) != NULL)
211			path_p = &(*path_p)->sp_next;
212		pathstr++;
213	}
214
215	for (;;) {
216		const char *bp = pathstr;
217		const char *ep = strchr(bp, ':');
218		if (ep == NULL)
219			ep = &pathstr[strlen(pathstr)];
220
221		path_p = _rtld_append_path(head_p, path_p, execname, bp, ep);
222
223		if (ep[0] == '\0')
224			break;
225		pathstr = ep + 1;
226	}
227}
228
229/*
230 * Process library mappings of the form:
231 *	<library_name>	<machdep_variable> <value,...:library_name,...> ...
232 */
233static void
234_rtld_process_mapping(Library_Xform **lib_p, const char *bp, const char *ep)
235{
236	Library_Xform *hwptr = NULL;
237	const char *ptr, *key, *ekey, *lib, *elib, *l;
238	int i, j;
239
240	dbg((" processing mapping \"%.*s\"", (int)(ep - bp), bp));
241
242	if ((ptr = getword(&bp, ep, WS)) == NULL || ptr == bp)
243		return;
244
245	dbg((" library \"%.*s\"", (int)(bp - ptr), ptr));
246
247	hwptr = xmalloc(sizeof(*hwptr));
248	memset(hwptr, 0, sizeof(*hwptr));
249	hwptr->name = exstrdup(ptr, bp);
250
251	bp++;
252
253	if ((ptr = getword(&bp, ep, WS)) == NULL || ptr == bp) {
254		xwarnx("missing sysctl variable name");
255		goto cleanup;
256	}
257
258	dbg((" sysctl \"%.*s\"", (int)(bp - ptr), ptr));
259
260	hwptr->ctlname = exstrdup(ptr, bp);
261
262	for (i = 0; bp++, (ptr = getword(&bp, ep, WS)) != NULL;) {
263		dbg((" ptr = %.*s", (int)(bp - ptr), ptr));
264		if (ptr == bp)
265			continue;
266
267		if (i == RTLD_MAX_ENTRY) {
268no_more:
269			xwarnx("maximum library entries exceeded `%s'",
270			    hwptr->name);
271			goto cleanup;
272		}
273		if ((key = getstr(&ptr, bp, ":")) == NULL) {
274			xwarnx("missing sysctl variable value for `%s'",
275			    hwptr->name);
276			goto cleanup;
277		}
278		ekey = ptr++;
279		if ((lib = getstr(&ptr, bp, ":")) == NULL) {
280			xwarnx("missing sysctl library list for `%s'",
281			    hwptr->name);
282			goto cleanup;
283		}
284		elib = ptr;		/* No need to advance */
285		for (j = 0; (l = getstr(&lib, elib, ",")) != NULL;
286		    j++, lib++) {
287			if (j == RTLD_MAX_LIBRARY) {
288				xwarnx("maximum library entries exceeded `%s'",
289				    hwptr->name);
290				goto cleanup;
291			}
292			dbg((" library \"%.*s\"", (int)(lib - l), l));
293			hwptr->entry[i].library[j] = exstrdup(l, lib);
294		}
295		if (j == 0) {
296			xwarnx("No library map entries for `%s/%.*s'",
297			    hwptr->name, (int)(bp - ptr), ptr);
298			goto cleanup;
299		}
300		j = i;
301		for (; (l = getstr(&key, ekey, ",")) != NULL; i++, key++) {
302			/*
303			 * Allow empty key (it is valid as string
304			 * value).  Thus, we loop at least once and
305			 * `i' is incremented.
306			 */
307
308			dbg((" key \"%.*s\"", (int)(key - l), l));
309			if (i == RTLD_MAX_ENTRY)
310				goto no_more;
311			if (i != j)
312				(void)memcpy(hwptr->entry[i].library,
313				    hwptr->entry[j].library,
314				    sizeof(hwptr->entry[j].library));
315			hwptr->entry[i].value = exstrdup(l, key);
316		}
317	}
318
319	if (i == 0) {
320		xwarnx("No library entries for `%s'", hwptr->name);
321		goto cleanup;
322	}
323
324	hwptr->next = *lib_p;
325	*lib_p = hwptr;
326
327	return;
328
329cleanup:
330	if (hwptr->name)
331		xfree(hwptr->name);
332	xfree(hwptr);
333}
334
335void
336_rtld_process_hints(const char *execname, Search_Path **path_p,
337    Library_Xform **lib_p, const char *fname)
338{
339	int fd;
340	char *buf, small[128];
341	const char *b, *ep, *ptr;
342	struct stat st;
343	ssize_t sz;
344	Search_Path **head_p = path_p;
345
346	if ((fd = open(fname, O_RDONLY)) == -1) {
347		/* Don't complain */
348		return;
349	}
350
351	/* Try to avoid mmap/stat on the file. */
352	buf = small;
353	buf[0] = '\0';
354	sz = read(fd, buf, sizeof(small));
355	if (sz == -1) {
356		xwarn("read: %s", fname);
357		(void)close(fd);
358		return;
359	}
360	if (sz >= (ssize_t)sizeof(small)) {
361		if (fstat(fd, &st) == -1) {
362			/* Complain */
363			xwarn("fstat: %s", fname);
364			(void)close(fd);
365			return;
366		}
367
368		sz = (ssize_t) st.st_size;
369
370		buf = mmap(0, sz, PROT_READ, MAP_SHARED|MAP_FILE, fd, 0);
371		if (buf == MAP_FAILED) {
372			xwarn("mmap: %s", fname);
373			(void)close(fd);
374			return;
375		}
376	}
377	(void)close(fd);
378
379	while ((*path_p) != NULL)
380		path_p = &(*path_p)->sp_next;
381
382	for (b = buf, ep = buf + sz; b < ep; b++) {
383		(void)getcstr(&b, ep, WS);
384		if (b == ep)
385			break;
386
387		ptr = getstr(&b, ep, "\n#");
388		if (*ptr == '/') {
389			/*
390			 * Since '/' != '\n' and != '#', we know ptr <
391			 * b.  And we will stop when b[-1] == '/'.
392			 */
393			while (b[-1] == ' ' || b[-1] == '\t')
394				b--;
395			path_p = _rtld_append_path(head_p, path_p, execname,
396			    ptr, b);
397		} else
398			_rtld_process_mapping(lib_p, ptr, b);
399
400		/*
401		 * b points one of ' ', \t, \n, # or equal to ep.  So,
402		 * make sure we are at newline or end of string.
403		 */
404		(void)getstr(&b, ep, "\n");
405	}
406
407	if (buf != small)
408		(void)munmap(buf, sz);
409}
410
411/* Basic name -> sysctl MIB translation */
412int
413_rtld_sysctl(const char *name, void *oldp, size_t *oldlen)
414{
415	const char *node, *ep;
416	struct sysctlnode query, *result, *newresult;
417	int mib[CTL_MAXNAME], r;
418	size_t res_size, n, i;
419	u_int miblen = 0;
420
421	/* Start with 16 entries, will grow it up as needed. */
422	res_size = 16 * sizeof(struct sysctlnode);
423	result = xmalloc(res_size);
424	if (result == NULL)
425		return (-1);
426
427	ep = name + strlen(name);
428	do {
429		i = ~0ul;
430		while (*name == '/' || *name == '.')
431			name++;
432		if (name >= ep)
433			break;
434
435		mib[miblen] = CTL_QUERY;
436		memset(&query, 0, sizeof(query));
437		query.sysctl_flags = SYSCTL_VERSION;
438
439		n = res_size;
440		if (sysctl(mib, miblen + 1, result, &n, &query,
441		    sizeof(query)) == -1) {
442			if (errno != ENOMEM)
443				goto bad;
444			/* Grow up result */
445			res_size = n;
446			newresult = xrealloc(result, res_size);
447			if (newresult == NULL)
448				goto bad;
449			result = newresult;
450			if (sysctl(mib, miblen + 1, result, &n, &query,
451			    sizeof(query)) == -1)
452				goto bad;
453		}
454		n /= sizeof(struct sysctlnode);
455
456		node = getstr(&name, ep, "./");
457
458		for (i = 0; i < n; i++)
459			if (matchstr(result[i].sysctl_name, node, name)) {
460				mib[miblen] = result[i].sysctl_num;
461				miblen++;
462				break;
463			}
464	} while (name < ep && miblen <= CTL_MAXNAME);
465
466	if (name < ep || i == ~0ul)
467		goto bad;
468	r = SYSCTL_TYPE(result[i].sysctl_flags);
469
470	xfree(result);
471	if (sysctl(mib, miblen, oldp, oldlen, NULL, 0) == -1)
472		return (-1);
473	return r;
474
475bad:
476	xfree(result);
477	return (-1);
478}
479