xref: /illumos-gate/usr/src/lib/abi/apptrace/common/abienv.c (revision fe4627ef755b7c263f91a0e6f07cdca5d7083501)
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