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