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
bugout(char const * call)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
build_env_list(Liblist ** list,char const * env)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
build_env_list1(Liblist ** list,Liblist ** listend,const char * env)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
env_to_intlist(Intlist ** list,char const * env)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
appendlist(Liblist ** list,Liblist ** listend,const char * name,int fatal)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 *
abibasename(const char * str)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 *
check_list(Liblist * list,char const * str)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
check_intlist(Intlist * list,char const * iface)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 *
checkenv(char const * env)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
build_interceptor_path(char * buf,size_t l,char const * path)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