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