1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/types.h> 28 #include <stdlib.h> 29 #include <unistd.h> 30 #include <string.h> 31 #include <stdio.h> 32 #include <dlfcn.h> 33 #include <errno.h> 34 #include <fnmatch.h> 35 #include <apptrace.h> 36 #include <libintl.h> 37 #include "abienv.h" 38 39 static char const *strdup_sym = "strdup"; 40 static char const *malloc_sym = "malloc"; 41 static char const *comma = ","; 42 43 static void 44 bugout(char const *call) 45 { 46 (void) fprintf(stderr, 47 dgettext(TEXT_DOMAIN, "apptrace: %s failed\n"), 48 call); 49 exit(EXIT_FAILURE); 50 } 51 52 void 53 build_env_list(Liblist **list, char const *env) 54 { 55 char *envstr; 56 char *tok; 57 58 if ((envstr = getenv(env)) == NULL) 59 return; 60 61 if ((envstr = strdup(envstr)) == NULL) 62 bugout(strdup_sym); 63 64 tok = strtok(envstr, comma); 65 while (tok != NULL) { 66 Liblist *lp; 67 68 if ((lp = malloc(sizeof (Liblist))) == NULL) 69 bugout(malloc_sym); 70 71 lp->l_libname = tok; 72 lp->l_next = *list; 73 *list = lp; 74 tok = strtok(NULL, comma); 75 } 76 } 77 78 void 79 build_env_list1(Liblist **list, Liblist **listend, const char *env) 80 { 81 char *envstr; 82 char *tok; 83 84 if ((envstr = getenv(env)) == NULL) 85 return; 86 87 /* 88 * It is possible that we have a single file name, 89 * in which case the subseqent loop will do nothing 90 */ 91 if (strchr(envstr, ',') == NULL) { 92 appendlist(list, listend, envstr, 1); 93 return; 94 } 95 96 if ((envstr = strdup(envstr)) == NULL) 97 bugout(strdup_sym); 98 99 tok = strtok(envstr, comma); 100 while (tok != NULL) { 101 appendlist(list, listend, tok, 1); 102 tok = strtok(NULL, comma); 103 } 104 free(envstr); 105 } 106 107 void 108 env_to_intlist(Intlist **list, char const *env) 109 { 110 char *envstr; 111 char *tok; 112 113 if ((envstr = getenv(env)) == NULL) 114 return; 115 116 if ((envstr = strdup(envstr)) == NULL) 117 bugout(strdup_sym); 118 119 for (tok = strtok(envstr, comma); 120 tok != NULL; 121 tok = strtok(NULL, comma)) { 122 123 Intlist *ip; 124 125 if ((ip = malloc(sizeof (Intlist))) == NULL) 126 bugout(malloc_sym); 127 128 if ((ip->i_name = strdup(tok)) == NULL) 129 bugout(strdup_sym); 130 131 ip->i_next = *list; 132 *list = ip; 133 } 134 free(envstr); 135 } 136 137 void 138 appendlist(Liblist **list, Liblist **listend, const char *name, int fatal) 139 { 140 Liblist *lp; 141 void *handle; 142 143 if (access(name, R_OK)) { 144 if (fatal) { 145 (void) fprintf(stderr, 146 dgettext(TEXT_DOMAIN, 147 "apptrace: %s: %s\n"), 148 name, 149 strerror(errno)); 150 exit(EXIT_FAILURE); 151 } 152 return; 153 } 154 155 if ((handle = dlopen(name, RTLD_LAZY)) == NULL) { 156 if (fatal) { 157 (void) fprintf(stderr, 158 dgettext(TEXT_DOMAIN, 159 "apptrace: dlopen on %s failed: %s\n"), 160 name, 161 dlerror()); 162 exit(EXIT_FAILURE); 163 } 164 return; 165 } 166 167 /* OK, so now add it to the end of the list */ 168 if ((lp = malloc(sizeof (Liblist))) == NULL) 169 bugout(malloc_sym); 170 171 if ((lp->l_libname = strdup(name)) == NULL) 172 bugout(strdup_sym); 173 lp->l_handle = handle; 174 lp->l_next = NULL; 175 if (*listend) 176 (*listend)->l_next = lp; 177 if (*list == NULL) 178 *list = lp; 179 *listend = lp; 180 } 181 182 /* 183 * Called abibasename() to avoid clash with basename(3C) 184 * Incidentally, basename(3C) is destructive which is why 185 * we are not using it instead. 186 */ 187 char * 188 abibasename(const char *str) 189 { 190 char *p; 191 192 if ((p = strrchr(str, '/')) != NULL) 193 return (p + 1); 194 else 195 return ((char *)str); 196 } 197 198 Liblist * 199 check_list(Liblist *list, char const *str) 200 { 201 char *basename1, *basename2, *p1, *p2; 202 Liblist *ret = NULL; 203 204 if (list == NULL) 205 return (NULL); 206 207 if ((basename2 = strdup(abibasename(str))) == NULL) 208 bugout(strdup_sym); 209 if ((p2 = strchr(basename2, '.')) != NULL) 210 *p2 = '\0'; 211 212 for (; list; list = list->l_next) { 213 /* Lose the dirname */ 214 if ((basename1 = strdup(abibasename(list->l_libname))) == NULL) 215 bugout(strdup_sym); 216 /* Lose the suffix */ 217 if ((p1 = strchr(basename1, '.')) != NULL) 218 *p1 = '\0'; 219 if (fnmatch(basename1, basename2, 0) == 0) { 220 ret = list; 221 free(basename1); 222 break; 223 } 224 free(basename1); 225 } 226 227 free(basename2); 228 return (ret); 229 } 230 231 int 232 check_intlist(Intlist *list, char const *iface) 233 { 234 if (list == NULL) 235 return (0); 236 237 for (; list != NULL; list = list->i_next) { 238 if (fnmatch(list->i_name, iface, 0) == 0) 239 return (1); 240 } 241 242 return (0); 243 } 244 245 char * 246 checkenv(char const *env) 247 { 248 char *envstr; 249 250 if ((envstr = getenv(env)) == NULL) 251 return (NULL); 252 while (*envstr == ' ') 253 envstr++; 254 if (*envstr == '\0') 255 return (NULL); 256 return (envstr); 257 } 258 259 int 260 build_interceptor_path(char *buf, size_t l, char const *path) 261 { 262 char *p, *t, *f; 263 #if defined(_LP64) 264 char *m; 265 #endif 266 int ret; 267 268 /* Duplicate the path */ 269 if ((p = strdup(path)) == NULL) 270 bugout(strdup_sym); 271 272 /* Find the last slash, if there ain't one bug out */ 273 if ((t = strrchr(p, '/')) == NULL) { 274 ret = 0; 275 goto done; 276 } 277 278 /* 279 * Wack the slash to a null byte. 280 * Thus if we got: 281 * /A/B/C/D.so.1 282 * p now points to /A/B/C 283 * f is set to point to D.so.1 284 */ 285 *t = '\0'; 286 f = ++t; 287 288 #if defined(_LP64) 289 /* 290 * As above except that in LP64 (for sparc) we'll get: 291 * /A/B/C/sparcv9/D.so.1 292 * thus p now points to: 293 * /A/B/C/sparcv9 294 * so we repeat the wack so that we get: 295 * /A/B/C 296 * and retain a pointer, m, to the machine dependent portion. 297 */ 298 if ((t = strrchr(p, '/')) == NULL) { 299 ret = 0; 300 goto done; 301 } 302 *t = '\0'; 303 m = ++t; 304 305 /* 306 * Now we can build a path name. 307 * This path is only a guess that'll be checked later in appendlist(). 308 * Some system libraries, like libc.so.1, reside in /lib while their 309 * corresponding abi_* counterparts reside in /usr/lib. The same is 310 * true for libraries like libc_psr.so.1 that reside in /platform 311 * rather than /usr/platform. To deal with this, we check whether 312 * the file in the direct path name we generate exists, and if not, 313 * we prepend "/usr" to it. This handles all existing cases. 314 */ 315 ret = snprintf(buf, l, "%s/abi/%s/abi_%s", p, m, f); 316 if (access(buf, R_OK) != 0 && strncmp(buf, "/usr/", 5) != 0) 317 ret = snprintf(buf, l, "/usr%s/abi/%s/abi_%s", p, m, f); 318 #else 319 ret = snprintf(buf, l, "%s/abi/abi_%s", p, f); 320 if (access(buf, R_OK) != 0 && strncmp(buf, "/usr/", 5) != 0) 321 ret = snprintf(buf, l, "/usr%s/abi/abi_%s", p, f); 322 #endif 323 324 done: 325 free(p); 326 return (ret); 327 } 328