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 #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 *, 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 *
exstrdup(const char * bp,const char * ep)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 *
getstr(const char ** p,const char * ep,const char * delim)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 *
getcstr(const char ** p,const char * ep,const char * delim)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 *
getword(const char ** p,const char * ep,const char * delim)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
matchstr(const char * p,const char * bp,const char * ep)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 *
_rtld_find_path(Search_Path * path,const char * pathstr,size_t pathlen)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 **
_rtld_append_path(Search_Path ** head_p,Search_Path ** path_p,const char * execname,const char * bp,const char * ep)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
198 void
_rtld_add_paths(const char * execname,Search_Path ** path_p,const char * pathstr)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 */
233 static void
_rtld_process_mapping(Library_Xform ** lib_p,const char * bp,const char * ep)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) {
268 no_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
329 cleanup:
330 if (hwptr->name)
331 xfree(hwptr->name);
332 xfree(hwptr);
333 }
334
335 void
_rtld_process_hints(const char * execname,Search_Path ** path_p,Library_Xform ** lib_p,const char * fname)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 */
412 int
_rtld_sysctl(const char * name,void * oldp,size_t * oldlen)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
475 bad:
476 xfree(result);
477 return (-1);
478 }
479