1 /* 2 * $FreeBSD$ 3 */ 4 5 #include <stdio.h> 6 #include <string.h> 7 #include <stdlib.h> 8 #include <sys/queue.h> 9 #include <sys/param.h> 10 11 #include "debug.h" 12 #include "rtld.h" 13 #include "libmap.h" 14 15 #ifndef _PATH_LIBMAP_CONF 16 #define _PATH_LIBMAP_CONF "/etc/libmap.conf" 17 #endif 18 19 #ifdef COMPAT_32BIT 20 #undef _PATH_LIBMAP_CONF 21 #define _PATH_LIBMAP_CONF "/etc/libmap32.conf" 22 #endif 23 24 TAILQ_HEAD(lm_list, lm); 25 struct lm { 26 char *f; 27 char *t; 28 29 TAILQ_ENTRY(lm) lm_link; 30 }; 31 32 TAILQ_HEAD(lmp_list, lmp) lmp_head = TAILQ_HEAD_INITIALIZER(lmp_head); 33 struct lmp { 34 char *p; 35 enum { T_EXACT=0, T_BASENAME, T_DIRECTORY } type; 36 struct lm_list lml; 37 TAILQ_ENTRY(lmp) lmp_link; 38 }; 39 40 static int lm_count; 41 42 static void lmc_parse (FILE *); 43 static void lm_add (const char *, const char *, const char *); 44 static void lm_free (struct lm_list *); 45 static char * lml_find (struct lm_list *, const char *); 46 static struct lm_list * lmp_find (const char *); 47 static struct lm_list * lmp_init (char *); 48 static const char * quickbasename (const char *); 49 static int readstrfn (void * cookie, char *buf, int len); 50 static int closestrfn (void * cookie); 51 52 #define iseol(c) (((c) == '#') || ((c) == '\0') || \ 53 ((c) == '\n') || ((c) == '\r')) 54 55 /* 56 * Do not use ctype.h macros, which rely on working TLS. It is 57 * too early to have thread-local variables functional. 58 */ 59 #define isspace1(c) ((c) == ' ' || (c) == '\t') 60 61 int 62 lm_init (char *libmap_override) 63 { 64 FILE *fp; 65 66 dbg("%s(\"%s\")", __func__, libmap_override); 67 68 TAILQ_INIT(&lmp_head); 69 70 fp = fopen(_PATH_LIBMAP_CONF, "r"); 71 if (fp) { 72 lmc_parse(fp); 73 fclose(fp); 74 } 75 76 if (libmap_override) { 77 char *p; 78 /* do some character replacement to make $LIBMAP look like a 79 text file, then "open" it with funopen */ 80 libmap_override = xstrdup(libmap_override); 81 82 for (p = libmap_override; *p; p++) { 83 switch (*p) { 84 case '=': 85 *p = ' '; break; 86 case ',': 87 *p = '\n'; break; 88 } 89 } 90 fp = funopen(libmap_override, readstrfn, NULL, NULL, closestrfn); 91 if (fp) { 92 lmc_parse(fp); 93 fclose(fp); 94 } 95 } 96 97 return (lm_count == 0); 98 } 99 100 static void 101 lmc_parse (FILE *fp) 102 { 103 char *cp; 104 char *f, *t, *c, *p; 105 char prog[MAXPATHLEN]; 106 char line[MAXPATHLEN + 2]; 107 108 dbg("%s(%p)", __func__, fp); 109 110 p = NULL; 111 while ((cp = fgets(line, MAXPATHLEN + 1, fp)) != NULL) { 112 t = f = c = NULL; 113 114 /* Skip over leading space */ 115 while (isspace1(*cp)) cp++; 116 117 /* Found a comment or EOL */ 118 if (iseol(*cp)) continue; 119 120 /* Found a constraint selector */ 121 if (*cp == '[') { 122 cp++; 123 124 /* Skip leading space */ 125 while (isspace1(*cp)) cp++; 126 127 /* Found comment, EOL or end of selector */ 128 if (iseol(*cp) || *cp == ']') 129 continue; 130 131 c = cp++; 132 /* Skip to end of word */ 133 while (!isspace1(*cp) && !iseol(*cp) && *cp != ']') 134 cp++; 135 136 /* Skip and zero out trailing space */ 137 while (isspace1(*cp)) *cp++ = '\0'; 138 139 /* Check if there is a closing brace */ 140 if (*cp != ']') continue; 141 142 /* Terminate string if there was no trailing space */ 143 *cp++ = '\0'; 144 145 /* 146 * There should be nothing except whitespace or comment 147 from this point to the end of the line. 148 */ 149 while(isspace1(*cp)) cp++; 150 if (!iseol(*cp)) continue; 151 152 strcpy(prog, c); 153 p = prog; 154 continue; 155 } 156 157 /* Parse the 'from' candidate. */ 158 f = cp++; 159 while (!isspace1(*cp) && !iseol(*cp)) cp++; 160 161 /* Skip and zero out the trailing whitespace */ 162 while (isspace1(*cp)) *cp++ = '\0'; 163 164 /* Found a comment or EOL */ 165 if (iseol(*cp)) continue; 166 167 /* Parse 'to' mapping */ 168 t = cp++; 169 while (!isspace1(*cp) && !iseol(*cp)) cp++; 170 171 /* Skip and zero out the trailing whitespace */ 172 while (isspace1(*cp)) *cp++ = '\0'; 173 174 /* Should be no extra tokens at this point */ 175 if (!iseol(*cp)) continue; 176 177 *cp = '\0'; 178 lm_add(p, f, t); 179 } 180 } 181 182 static void 183 lm_free (struct lm_list *lml) 184 { 185 struct lm *lm; 186 187 dbg("%s(%p)", __func__, lml); 188 189 while (!TAILQ_EMPTY(lml)) { 190 lm = TAILQ_FIRST(lml); 191 TAILQ_REMOVE(lml, lm, lm_link); 192 free(lm->f); 193 free(lm->t); 194 free(lm); 195 } 196 return; 197 } 198 199 void 200 lm_fini (void) 201 { 202 struct lmp *lmp; 203 204 dbg("%s()", __func__); 205 206 while (!TAILQ_EMPTY(&lmp_head)) { 207 lmp = TAILQ_FIRST(&lmp_head); 208 TAILQ_REMOVE(&lmp_head, lmp, lmp_link); 209 free(lmp->p); 210 lm_free(&lmp->lml); 211 free(lmp); 212 } 213 return; 214 } 215 216 static void 217 lm_add (const char *p, const char *f, const char *t) 218 { 219 struct lm_list *lml; 220 struct lm *lm; 221 222 if (p == NULL) 223 p = "$DEFAULT$"; 224 225 dbg("%s(\"%s\", \"%s\", \"%s\")", __func__, p, f, t); 226 227 if ((lml = lmp_find(p)) == NULL) 228 lml = lmp_init(xstrdup(p)); 229 230 lm = xmalloc(sizeof(struct lm)); 231 lm->f = xstrdup(f); 232 lm->t = xstrdup(t); 233 TAILQ_INSERT_HEAD(lml, lm, lm_link); 234 lm_count++; 235 } 236 237 char * 238 lm_find (const char *p, const char *f) 239 { 240 struct lm_list *lml; 241 char *t; 242 243 dbg("%s(\"%s\", \"%s\")", __func__, p, f); 244 245 if (p != NULL && (lml = lmp_find(p)) != NULL) { 246 t = lml_find(lml, f); 247 if (t != NULL) { 248 /* 249 * Add a global mapping if we have 250 * a successful constrained match. 251 */ 252 lm_add(NULL, f, t); 253 return (t); 254 } 255 } 256 lml = lmp_find("$DEFAULT$"); 257 if (lml != NULL) 258 return (lml_find(lml, f)); 259 else 260 return (NULL); 261 } 262 263 /* Given a libmap translation list and a library name, return the 264 replacement library, or NULL */ 265 #ifdef COMPAT_32BIT 266 char * 267 lm_findn (const char *p, const char *f, const int n) 268 { 269 char pathbuf[64], *s, *t; 270 271 if (n < sizeof(pathbuf) - 1) 272 s = pathbuf; 273 else 274 s = xmalloc(n + 1); 275 memcpy(s, f, n); 276 s[n] = '\0'; 277 t = lm_find(p, s); 278 if (s != pathbuf) 279 free(s); 280 return (t); 281 } 282 #endif 283 284 static char * 285 lml_find (struct lm_list *lmh, const char *f) 286 { 287 struct lm *lm; 288 289 dbg("%s(%p, \"%s\")", __func__, lmh, f); 290 291 TAILQ_FOREACH(lm, lmh, lm_link) 292 if (strcmp(f, lm->f) == 0) 293 return (lm->t); 294 return (NULL); 295 } 296 297 /* Given an executable name, return a pointer to the translation list or 298 NULL if no matches */ 299 static struct lm_list * 300 lmp_find (const char *n) 301 { 302 struct lmp *lmp; 303 304 dbg("%s(\"%s\")", __func__, n); 305 306 TAILQ_FOREACH(lmp, &lmp_head, lmp_link) 307 if ((lmp->type == T_EXACT && strcmp(n, lmp->p) == 0) || 308 (lmp->type == T_DIRECTORY && strncmp(n, lmp->p, strlen(lmp->p)) == 0) || 309 (lmp->type == T_BASENAME && strcmp(quickbasename(n), lmp->p) == 0)) 310 return (&lmp->lml); 311 return (NULL); 312 } 313 314 static struct lm_list * 315 lmp_init (char *n) 316 { 317 struct lmp *lmp; 318 319 dbg("%s(\"%s\")", __func__, n); 320 321 lmp = xmalloc(sizeof(struct lmp)); 322 lmp->p = n; 323 if (n[strlen(n)-1] == '/') 324 lmp->type = T_DIRECTORY; 325 else if (strchr(n,'/') == NULL) 326 lmp->type = T_BASENAME; 327 else 328 lmp->type = T_EXACT; 329 TAILQ_INIT(&lmp->lml); 330 TAILQ_INSERT_HEAD(&lmp_head, lmp, lmp_link); 331 332 return (&lmp->lml); 333 } 334 335 /* libc basename is overkill. Return a pointer to the character after the 336 last /, or the original string if there are no slashes. */ 337 static const char * 338 quickbasename (const char *path) 339 { 340 const char *p = path; 341 for (; *path; path++) { 342 if (*path == '/') 343 p = path+1; 344 } 345 return (p); 346 } 347 348 static int 349 readstrfn(void * cookie, char *buf, int len) 350 { 351 static char *current; 352 static int left; 353 int copied; 354 355 copied = 0; 356 if (!current) { 357 current = cookie; 358 left = strlen(cookie); 359 } 360 while (*current && left && len) { 361 *buf++ = *current++; 362 left--; 363 len--; 364 copied++; 365 } 366 return copied; 367 } 368 369 static int 370 closestrfn(void * cookie) 371 { 372 free(cookie); 373 return 0; 374 } 375