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