1 /* $NetBSD: ktrace.c,v 1.45 2011/09/16 15:39:26 joerg Exp $ */ 2 3 /*- 4 * Copyright (c) 1988, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1988, 1993\ 35 The Regents of the University of California. All rights reserved."); 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)ktrace.c 8.2 (Berkeley) 4/28/95"; 41 #else 42 __RCSID("$NetBSD: ktrace.c,v 1.45 2011/09/16 15:39:26 joerg Exp $"); 43 #endif 44 #endif /* not lint */ 45 46 #include <sys/param.h> 47 #include <sys/stat.h> 48 #include <sys/wait.h> 49 #include <sys/file.h> 50 #include <sys/time.h> 51 #include <sys/uio.h> 52 #include <sys/ktrace.h> 53 #include <sys/socket.h> 54 55 #include <err.h> 56 #include <errno.h> 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <unistd.h> 61 #include <signal.h> 62 63 #include "ktrace.h" 64 65 #ifdef KTRUSS 66 #include "setemul.h" 67 #endif 68 69 static int rpid(char *); 70 __dead static void usage(void); 71 static int do_ktrace(const char *, int, int, int, int, int); 72 __dead static void no_ktrace(int); 73 static void fset(int fd, int flag); 74 static void fclear(int fd, int flag); 75 76 #ifdef KTRUSS 77 extern int timestamp, decimal, fancy, tail, maxdata; 78 #endif 79 80 int 81 main(int argc, char *argv[]) 82 { 83 enum { NOTSET, CLEAR, CLEARALL } clear; 84 int block, append, ch, fd, trset, ops, pid, pidset, synclog, trpoints; 85 int vers; 86 const char *outfile; 87 #ifdef KTRUSS 88 const char *infile; 89 const char *emul_name = "netbsd"; 90 #endif 91 92 clear = NOTSET; 93 append = ops = pidset = trset = synclog = 0; 94 trpoints = 0; 95 block = 1; 96 vers = 2; 97 pid = 0; /* Appease GCC */ 98 99 #ifdef KTRUSS 100 # define OPTIONS "aCce:df:g:ilm:no:p:RTt:v:" 101 outfile = infile = NULL; 102 #else 103 # define OPTIONS "aCcdf:g:ip:st:v:" 104 outfile = DEF_TRACEFILE; 105 #endif 106 107 while ((ch = getopt(argc, argv, OPTIONS)) != -1) 108 switch (ch) { 109 case 'a': 110 append = 1; 111 break; 112 case 'C': 113 clear = CLEARALL; 114 pidset = 1; 115 break; 116 case 'c': 117 clear = CLEAR; 118 pidset = 1; 119 break; 120 case 'd': 121 ops |= KTRFLAG_DESCEND; 122 break; 123 #ifdef KTRUSS 124 case 'e': 125 emul_name = strdup(optarg); /* it's safer to copy it */ 126 break; 127 case 'f': 128 infile = optarg; 129 break; 130 #else 131 case 'f': 132 outfile = optarg; 133 break; 134 #endif 135 case 'g': 136 pid = -rpid(optarg); 137 pidset = 1; 138 break; 139 case 'i': 140 trpoints |= KTRFAC_INHERIT; 141 break; 142 #ifdef KTRUSS 143 case 'l': 144 tail = 1; 145 break; 146 case 'm': 147 maxdata = atoi(optarg); 148 break; 149 case 'o': 150 outfile = optarg; 151 break; 152 #endif 153 case 'n': 154 block = 0; 155 break; 156 case 'p': 157 pid = rpid(optarg); 158 pidset = 1; 159 break; 160 #ifdef KTRUSS 161 case 'R': 162 timestamp = 2; /* relative timestamp */ 163 break; 164 #else 165 case 's': 166 synclog = 1; 167 break; 168 #endif 169 #ifdef KTRUSS 170 case 'T': 171 timestamp = 1; 172 break; 173 #endif 174 case 't': 175 trset = 1; 176 trpoints = getpoints(trpoints, optarg); 177 if (trpoints < 0) { 178 warnx("unknown facility in %s", optarg); 179 usage(); 180 } 181 break; 182 case 'v': 183 vers = atoi(optarg); 184 break; 185 default: 186 usage(); 187 } 188 argv += optind; 189 argc -= optind; 190 191 if (!trset) 192 trpoints |= clear == NOTSET ? DEF_POINTS : ALL_POINTS; 193 194 if ((pidset && *argv) || (!pidset && !*argv)) { 195 #ifdef KTRUSS 196 if (!infile) 197 #endif 198 usage(); 199 } 200 201 #ifdef KTRUSS 202 if (clear == CLEAR && outfile == NULL && pid == 0) 203 usage(); 204 205 if (infile) { 206 dumpfile(infile, 0, trpoints); 207 exit(0); 208 } 209 210 setemul(emul_name, 0, 0); 211 #endif 212 213 /* 214 * For cleaner traces, initialize malloc now rather 215 * than in a traced subprocess. 216 */ 217 free(malloc(1)); 218 219 (void)signal(SIGSYS, no_ktrace); 220 if (clear != NOTSET) { 221 if (clear == CLEARALL) { 222 ops = KTROP_CLEAR | KTRFLAG_DESCEND; 223 trpoints = ALL_POINTS; 224 pid = 1; 225 } else 226 ops |= pid ? KTROP_CLEAR : KTROP_CLEARFILE; 227 228 (void)do_ktrace(outfile, vers, ops, trpoints, pid, block); 229 exit(0); 230 } 231 232 if (outfile && strcmp(outfile, "-")) { 233 if ((fd = open(outfile, O_CREAT | O_WRONLY | 234 (append ? 0 : O_TRUNC) | (synclog ? 0 : O_SYNC), 235 DEFFILEMODE)) < 0) 236 err(EXIT_FAILURE, "%s", outfile); 237 (void)close(fd); 238 } 239 240 if (*argv) { 241 #ifdef KTRUSS 242 if (do_ktrace(outfile, vers, ops, trpoints, getpid(), block) == 1) { 243 execvp(argv[0], &argv[0]); 244 err(EXIT_FAILURE, "exec of '%s' failed", argv[0]); 245 } 246 #else 247 (void)do_ktrace(outfile, vers, ops, trpoints, getpid(), block); 248 execvp(argv[0], &argv[0]); 249 err(EXIT_FAILURE, "exec of '%s' failed", argv[0]); 250 #endif 251 } else 252 (void)do_ktrace(outfile, vers, ops, trpoints, pid, block); 253 return 0; 254 } 255 256 static int 257 rpid(char *p) 258 { 259 static int first; 260 261 if (first++) { 262 warnx("only one -g or -p flag is permitted."); 263 usage(); 264 } 265 if (!*p) { 266 warnx("illegal process id."); 267 usage(); 268 } 269 return (atoi(p)); 270 } 271 272 static void 273 fset(int fd, int flag) 274 { 275 int oflag = fcntl(fd, F_GETFL, 0); 276 277 if (oflag == -1) 278 err(EXIT_FAILURE, "Cannot get file flags"); 279 if (fcntl(fd, F_SETFL, oflag | flag) == -1) 280 err(EXIT_FAILURE, "Cannot set file flags"); 281 } 282 283 static void 284 fclear(int fd, int flag) 285 { 286 int oflag = fcntl(fd, F_GETFL, 0); 287 288 if (oflag == -1) 289 err(EXIT_FAILURE, "Cannot get file flags"); 290 if (fcntl(fd, F_SETFL, oflag & ~flag) == -1) 291 err(EXIT_FAILURE, "Cannot set file flags"); 292 } 293 294 static void 295 usage(void) 296 { 297 298 #define TRPOINTS "[AaceilmnSsuvw+-]" 299 #ifdef KTRUSS 300 (void)fprintf(stderr, "usage:\t%s " 301 "[-aCcdilnRT] [-e emulation] [-f infile] [-g pgrp] " 302 "[-m maxdata]\n\t " 303 "[-o outfile] [-p pid] [-t " TRPOINTS "]\n", getprogname()); 304 (void)fprintf(stderr, "\t%s " 305 "[-adinRT] [-e emulation] [-m maxdata] [-o outfile]\n\t " 306 "[-t " TRPOINTS "] [-v vers] command\n", 307 getprogname()); 308 #else 309 (void)fprintf(stderr, "usage:\t%s " 310 "[-aCcdins] [-f trfile] [-g pgrp] [-p pid] [-t " TRPOINTS "]\n", 311 getprogname()); 312 (void)fprintf(stderr, "\t%s " 313 "[-adis] [-f trfile] [-t " TRPOINTS "] command\n", 314 getprogname()); 315 #endif 316 exit(1); 317 } 318 319 static const char *ktracefile = NULL; 320 static void 321 /*ARGSUSED*/ 322 no_ktrace(int sig) 323 { 324 325 if (ktracefile) 326 (void)unlink(ktracefile); 327 (void)errx(EXIT_FAILURE, 328 "ktrace(2) system call not supported in the running" 329 " kernel; re-compile kernel with `options KTRACE'"); 330 } 331 332 static int 333 do_ktrace(const char *tracefile, int vers, int ops, int trpoints, int pid, 334 int block) 335 { 336 int ret; 337 ops |= vers << KTRFAC_VER_SHIFT; 338 339 if (KTROP(ops) == KTROP_SET && 340 (!tracefile || strcmp(tracefile, "-") == 0)) { 341 int pi[2], dofork; 342 343 if (pipe(pi) < 0) 344 err(EXIT_FAILURE, "pipe(2)"); 345 346 fset(pi[0], FD_CLOEXEC); 347 fset(pi[1], FD_CLOEXEC); 348 dofork = (pid == getpid()); 349 350 if (dofork) { 351 #ifdef KTRUSS 352 /* 353 * Create a child process and trace it. 354 */ 355 pid = fork(); 356 if (pid == -1) 357 err(EXIT_FAILURE, "fork"); 358 else if (pid == 0) { 359 pid = getpid(); 360 goto trace_and_exec; 361 } 362 #else 363 int fpid; 364 365 /* 366 * Create a dumper process and we will be 367 * traced. 368 */ 369 fpid = fork(); 370 if (fpid == -1) 371 err(EXIT_FAILURE, "fork"); 372 else if (fpid != 0) 373 goto trace_and_exec; 374 #endif 375 (void)close(pi[1]); 376 } else { 377 ret = fktrace(pi[1], ops, trpoints, pid); 378 if (ret == -1) 379 err(EXIT_FAILURE, "fd %d, pid %d", 380 pi[1], pid); 381 if (block) 382 fclear(pi[1], O_NONBLOCK); 383 } 384 #ifdef KTRUSS 385 dumpfile(NULL, pi[0], trpoints); 386 waitpid(pid, NULL, 0); 387 #else 388 { 389 char buf[BUFSIZ]; 390 int n; 391 392 while ((n = 393 read(pi[0], buf, sizeof(buf))) > 0) 394 if (write(STDOUT_FILENO, buf, (size_t)n) == -1) 395 warn("write failed"); 396 } 397 if (dofork) 398 _exit(0); 399 #endif 400 return 0; 401 402 trace_and_exec: 403 (void)close(pi[0]); 404 ret = fktrace(pi[1], ops, trpoints, pid); 405 if (ret == -1) 406 err(EXIT_FAILURE, "fd %d, pid %d", pi[1], pid); 407 if (block) 408 fclear(pi[1], O_NONBLOCK); 409 } else { 410 ret = ktrace(ktracefile = tracefile, ops, trpoints, pid); 411 if (ret == -1) 412 err(EXIT_FAILURE, "file %s, pid %d", 413 tracefile != NULL ? tracefile : "NULL", pid); 414 } 415 return 1; 416 } 417