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