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/*
24 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
25 * Use is subject to license terms.
26 */
27
28#pragma ident	"%Z%%M%	%I%	%E% SMI"
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <unistd.h>
33#include <string.h>
34#include <errno.h>
35#include <sys/wait.h>
36#include <apptrace.h>
37#include <libintl.h>
38#include <locale.h>
39
40#ifdef TRUE
41#undef TRUE
42#endif
43#ifdef FALSE
44#undef FALSE
45#endif
46#define	TRUE  1
47#define	FALSE 0
48
49/* Various list pointers */
50static char *fromlist;
51static char *fromexcl;
52static char *tolist;
53static char *toexcl;
54
55static char *iflist;
56static char *ifexcl;
57static char *viflist;
58static char *vifexcl;
59
60/* The supported options */
61static char const *optlet = "F:fo:T:t:v:";
62/* basename(argv[0]) */
63static char const *command;
64
65/* The environment variables that'll get picked up by apptrace.so.1 */
66static char const *APPTRACE_BINDTO = "APPTRACE_BINDTO=";
67static char const *APPTRACE_BINDTO_EXCLUDE = "APPTRACE_BINDTO_EXCLUDE=";
68static char const *APPTRACE_BINDFROM = "APPTRACE_BINDFROM=";
69static char const *APPTRACE_BINDFROM_EXCLUDE = "APPTRACE_BINDFROM_EXCLUDE=";
70static char const *APPTRACE_OUTPUT = "APPTRACE_OUTPUT=";
71static char const *APPTRACE_PID = "APPTRACE_PID=";
72static char const *APPTRACE_INTERFACES = "APPTRACE_INTERFACES=";
73static char const *APPTRACE_INTERFACES_EXCLUDE = "APPTRACE_INTERFACES_EXCLUDE=";
74static char const *APPTRACE_VERBOSE = "APPTRACE_VERBOSE=";
75static char const *APPTRACE_VERBOSE_EXCLUDE = "APPTRACE_VERBOSE_EXCLUDE=";
76
77/* Some default values for the above */
78static char *LD_AUDIT = "LD_AUDIT=/usr/lib/abi/apptrace.so.1";
79#if	defined(sparc) || defined(__sparcv9)
80static char *LD_AUDIT_64 =
81	"LD_AUDIT_64=/usr/lib/abi/sparcv9/apptrace.so.1";
82#elif	defined(i386) || defined(__amd64)
83static char *LD_AUDIT_64 =
84	"LD_AUDIT_64=/usr/lib/abi/amd64/apptrace.so.1";
85#else
86#error Unsupported Platform
87#endif
88
89static char const *one = "1";
90
91/* The local support functions */
92static void usage(char const *);
93static void stuffenv(char const *, char const *);
94static char *buildlist(char **, char const *);
95
96int
97main(int argc, char **argv)
98{
99	int	opt;
100	int	fflag = FALSE;
101	int	errflg = FALSE;
102	char	*outfile = NULL;
103	int	stat_loc;
104	pid_t	wret, pid;
105
106	(void) setlocale(LC_ALL, "");
107#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
108#define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
109#endif
110	(void) textdomain(TEXT_DOMAIN);
111
112
113	/* Squirrel the basename of the command name away. */
114	if ((command = strrchr(argv[0], '/')) != NULL)
115		command++;
116	else
117		command = argv[0];
118
119	while ((opt = getopt(argc, argv, optlet)) != EOF) {
120		switch (opt) {
121		case 'F':
122			if (*optarg == '!')
123				(void) buildlist(&fromexcl, optarg + 1);
124			else
125				(void) buildlist(&fromlist, optarg);
126			break;
127		case 'f':
128			fflag = TRUE;
129			break;
130		case 'o':
131			outfile = optarg;
132			break;
133		case 'T':
134			if (*optarg == '!')
135				(void) buildlist(&toexcl, optarg + 1);
136			else
137				(void) buildlist(&tolist, optarg);
138			break;
139		case 't':
140			if (*optarg == '!')
141				(void) buildlist(&ifexcl, optarg + 1);
142			else
143				(void) buildlist(&iflist, optarg);
144			break;
145		case 'v':
146			if (*optarg == '!')
147				(void) buildlist(&vifexcl, optarg + 1);
148			else
149				(void) buildlist(&viflist, optarg);
150			break;
151		default:
152			errflg = TRUE;
153			break;
154		}
155	}
156
157	/*
158	 * Whack the argument vector so that the remainder will be
159	 * ready for passing to exec
160	 */
161	argc -= optind;
162	argv += optind;
163
164	/*
165	 * If there was a problem with the options, or there was no command
166	 * to be run, then give the usage message and bugout.
167	 */
168	if (errflg || argc <= 0) {
169		usage(command);
170		exit(EXIT_FAILURE);
171	}
172
173	/*
174	 * This is where the environment gets setup.
175	 */
176	if (fflag == TRUE)
177		stuffenv(APPTRACE_PID, one);
178
179	if (fromexcl != NULL)
180		stuffenv(APPTRACE_BINDFROM_EXCLUDE, fromexcl);
181	if (fromlist != NULL)
182		stuffenv(APPTRACE_BINDFROM, fromlist);
183
184	if (tolist != NULL)
185		stuffenv(APPTRACE_BINDTO, tolist);
186	if (toexcl != NULL)
187		stuffenv(APPTRACE_BINDTO_EXCLUDE, toexcl);
188
189	if (iflist != NULL)
190		stuffenv(APPTRACE_INTERFACES, iflist);
191	if (ifexcl != NULL)
192		stuffenv(APPTRACE_INTERFACES_EXCLUDE, ifexcl);
193
194	if (viflist != NULL)
195		stuffenv(APPTRACE_VERBOSE, viflist);
196	if (vifexcl != NULL)
197		stuffenv(APPTRACE_VERBOSE_EXCLUDE, vifexcl);
198
199	if (outfile != NULL)
200		stuffenv(APPTRACE_OUTPUT, outfile);
201
202	/*
203	 * It is the setting of the LD_AUDIT environment variable
204	 * that tells ld.so.1 to enable link auditing when the child
205	 * is exec()ed.
206	 */
207	(void) putenv(LD_AUDIT);
208	(void) putenv(LD_AUDIT_64);
209
210	/*
211	 * The environment is now all setup.
212	 * For those about to rock, we salute you!
213	 */
214	pid = fork();
215	switch (pid) {
216		/* Error */
217	case -1:
218		(void) fprintf(stderr, gettext("%s: fork failed: %s\n"),
219		    command, strerror(errno));
220		exit(EXIT_FAILURE);
221		break;
222		/* Child */
223	case 0:
224		/*
225		 * Usual failure is argv[0] does not exist or is
226		 * not executable.
227		 */
228		if (execvp(argv[0], argv)) {
229			(void) fprintf(stderr, gettext("%s: %s: %s\n"),
230			    command, argv[0], strerror(errno));
231			_exit(EXIT_FAILURE);
232		}
233		break;
234		/* Parent */
235	default:
236		wret = waitpid(pid, &stat_loc, 0);
237		if (wret == -1) {
238			(void) fprintf(stderr,
239			    gettext("%s: waitpid failed: %s\n"),
240			    command, strerror(errno));
241			exit(EXIT_FAILURE);
242		}
243
244		if (wret != pid) {
245			(void) fprintf(stderr,
246			    gettext("%s: "
247			    "waitpid returned %ld when child pid was %ld\n"),
248			    command, wret, pid);
249			exit(EXIT_FAILURE);
250		}
251
252		if (WIFSIGNALED(stat_loc)) {
253			(void) fprintf(stderr, gettext("\n%s: %s: %s"),
254			    command, argv[0], strsignal(WTERMSIG(stat_loc)));
255			if (WCOREDUMP(stat_loc)) {
256				(void) fputs(gettext("(Core dump)"), stderr);
257#ifdef DEBUG
258				(void) fputs(gettext("\nRunning pstack:\n"),
259				    stderr);
260				(void) putenv("LD_AUDIT=");
261				(void) putenv("LD_AUDIT_64=");
262				(void) system("/usr/proc/bin/pstack core");
263#endif
264			}
265			(void) putc('\n', stderr);
266		}
267
268		/* Normal return from main() */
269		return (WEXITSTATUS(stat_loc));
270	}
271	return (0);
272	/* NOTREACHED */
273}
274
275/*
276 * Take a string in the form "VAR=" and another in the
277 * form "value" and paste them together.
278 */
279static void
280stuffenv(char const *var, char const *val)
281{
282	int lenvar, lenval;
283	char *stuff;
284
285	lenvar = strlen(var);
286	lenval = strlen(val);
287
288	if ((stuff = malloc(lenvar + lenval + 1)) == NULL) {
289		(void) fprintf(stderr, gettext("%s: malloc failed\n"), command);
290		exit(EXIT_FAILURE);
291	}
292	(void) sprintf(stuff, "%s%s", var, val);
293	(void) putenv(stuff);
294}
295
296/*
297 * If *dst is empty, use strdup to duplicate src.
298 * Otherwise:  dst = dst + "," + src;
299 */
300static char *
301buildlist(char **dst, char const *src)
302{
303	int len;
304	char *p;
305
306	/*
307	 * If dst is still empty then dup,
308	 * if dup succeeds set dst.
309	 */
310	if (*dst == NULL) {
311		p = strdup(src);
312		if (p == NULL)
313			goto error;
314		*dst = p;
315		return (p);
316	}
317
318	len = strlen(*dst);
319
320	/* +2 because of the comma we add below */
321	if ((p = realloc(*dst, len + strlen(src) + 2)) == NULL)
322		goto error;
323
324	*dst = p;
325
326	*(*dst + len) = ',';
327	(void) strcpy((*dst + len + 1), src);
328
329	return (*dst);
330
331error:
332	(void) fprintf(stderr, gettext("%s: allocation failed: %s\n"),
333	    command, strerror(errno));
334	exit(EXIT_FAILURE);
335	/* NOTREACHED */
336}
337
338static void
339usage(char const *prog)
340{
341	(void) fprintf(stderr, gettext("Usage: %s [-f][-F [!]tracefromlist]"
342	    "[-T [!]tracetolist][-o outputfile]\n"
343	    "	[-t calls][-v calls] prog [prog arguments]\n"
344
345	    "	-F <bindfromlist>\n"
346	    "		A comma separated list of libraries that are to be\n"
347	    "		traced.  Only calls from these libraries will be\n"
348	    "		traced.  The default is to trace calls from the\n"
349	    "		main executable.\n"
350	    "		If <bindfromlist> begins with a ! then it defines\n"
351	    "		a list of libraries to exclude from the trace.\n"
352	    "	-T <bindtolist>\n"
353	    "		A comma separated list of libraries that are to be\n"
354	    "		traced.  Only calls to these libraries will be\n"
355	    "		traced.  The default is to trace all calls.\n"
356	    "		If <bindtolist> begins with a ! then it defines\n"
357	    "		a list of libraries to exclude from the trace.\n"
358	    "	-o <outputfile>\n"
359	    "		%s output will be directed to 'outputfile'.\n"
360	    "		by default it is placed on stderr\n"
361	    "	-f\n"
362	    "		Follow all children created by fork() and also\n"
363	    "		print apptrace output for the children.  This also\n"
364	    "		causes a 'pid' to be added to each output line\n"
365	    "	-t <tracelist>\n"
366	    "		A comma separated list of interfaces to trace.\n"
367	    "		A list preceded by ! is an exlusion list.\n"
368	    "	-v <verboselist>\n"
369	    "		A comma separated list of interfaces to trace\n"
370	    "		verbosely.\n"
371	    "		A list preceded by ! is an exclusion list.\n"
372	    "		Interfaces matched in -v do not also need to be\n"
373	    "		named by -t\n"
374	    "	All lists may use shell style wild cards.\n"
375	    "	Leading path components or suffixes are not required when\n"
376	    "	listing libraries (ie. libc will match /usr/lib/libc.so.1).\n"),
377	    prog, prog);
378}
379