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 */
50 static char *fromlist;
51 static char *fromexcl;
52 static char *tolist;
53 static char *toexcl;
54
55 static char *iflist;
56 static char *ifexcl;
57 static char *viflist;
58 static char *vifexcl;
59
60 /* The supported options */
61 static char const *optlet = "F:fo:T:t:v:";
62 /* basename(argv[0]) */
63 static char const *command;
64
65 /* The environment variables that'll get picked up by apptrace.so.1 */
66 static char const *APPTRACE_BINDTO = "APPTRACE_BINDTO=";
67 static char const *APPTRACE_BINDTO_EXCLUDE = "APPTRACE_BINDTO_EXCLUDE=";
68 static char const *APPTRACE_BINDFROM = "APPTRACE_BINDFROM=";
69 static char const *APPTRACE_BINDFROM_EXCLUDE = "APPTRACE_BINDFROM_EXCLUDE=";
70 static char const *APPTRACE_OUTPUT = "APPTRACE_OUTPUT=";
71 static char const *APPTRACE_PID = "APPTRACE_PID=";
72 static char const *APPTRACE_INTERFACES = "APPTRACE_INTERFACES=";
73 static char const *APPTRACE_INTERFACES_EXCLUDE = "APPTRACE_INTERFACES_EXCLUDE=";
74 static char const *APPTRACE_VERBOSE = "APPTRACE_VERBOSE=";
75 static char const *APPTRACE_VERBOSE_EXCLUDE = "APPTRACE_VERBOSE_EXCLUDE=";
76
77 /* Some default values for the above */
78 static char *LD_AUDIT = "LD_AUDIT=/usr/lib/abi/apptrace.so.1";
79 #if defined(sparc) || defined(__sparcv9)
80 static char *LD_AUDIT_64 =
81 "LD_AUDIT_64=/usr/lib/abi/sparcv9/apptrace.so.1";
82 #elif defined(i386) || defined(__amd64)
83 static char *LD_AUDIT_64 =
84 "LD_AUDIT_64=/usr/lib/abi/amd64/apptrace.so.1";
85 #else
86 #error Unsupported Platform
87 #endif
88
89 static char const *one = "1";
90
91 /* The local support functions */
92 static void usage(char const *);
93 static void stuffenv(char const *, char const *);
94 static char *buildlist(char **, char const *);
95
96 int
main(int argc,char ** argv)97 main(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 */
279 static void
stuffenv(char const * var,char const * val)280 stuffenv(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 */
300 static char *
buildlist(char ** dst,char const * src)301 buildlist(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
331 error:
332 (void) fprintf(stderr, gettext("%s: allocation failed: %s\n"),
333 command, strerror(errno));
334 exit(EXIT_FAILURE);
335 /* NOTREACHED */
336 }
337
338 static void
usage(char const * prog)339 usage(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