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 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 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 * 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 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