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