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