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