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