1 /* $NetBSD: ktrace.c,v 1.44 2011/02/05 15:22:17 yamt 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.44 2011/02/05 15:22:17 yamt 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 int main(int, char *[]); 70 static int rpid(char *); 71 static void usage(void); 72 static int do_ktrace(const char *, int, int, int, int, int); 73 static void no_ktrace(int); 74 static void fset(int fd, int flag); 75 static void fclear(int fd, int flag); 76 77 #ifdef KTRUSS 78 extern int timestamp, decimal, fancy, tail, maxdata; 79 #endif 80 81 int 82 main(int argc, char *argv[]) 83 { 84 enum { NOTSET, CLEAR, CLEARALL } clear; 85 int block, append, ch, fd, trset, ops, pid, pidset, synclog, trpoints; 86 int vers; 87 const char *outfile; 88 #ifdef KTRUSS 89 const char *infile; 90 const char *emul_name = "netbsd"; 91 #endif 92 93 clear = NOTSET; 94 append = ops = pidset = trset = synclog = 0; 95 trpoints = 0; 96 block = 1; 97 vers = 2; 98 pid = 0; /* Appease GCC */ 99 100 #ifdef KTRUSS 101 # define OPTIONS "aCce:df:g:ilm:no:p:RTt:v:" 102 outfile = infile = NULL; 103 #else 104 # define OPTIONS "aCcdf:g:ip:st:v:" 105 outfile = DEF_TRACEFILE; 106 #endif 107 108 while ((ch = getopt(argc, argv, OPTIONS)) != -1) 109 switch (ch) { 110 case 'a': 111 append = 1; 112 break; 113 case 'C': 114 clear = CLEARALL; 115 pidset = 1; 116 break; 117 case 'c': 118 clear = CLEAR; 119 pidset = 1; 120 break; 121 case 'd': 122 ops |= KTRFLAG_DESCEND; 123 break; 124 #ifdef KTRUSS 125 case 'e': 126 emul_name = strdup(optarg); /* it's safer to copy it */ 127 break; 128 case 'f': 129 infile = optarg; 130 break; 131 #else 132 case 'f': 133 outfile = optarg; 134 break; 135 #endif 136 case 'g': 137 pid = -rpid(optarg); 138 pidset = 1; 139 break; 140 case 'i': 141 trpoints |= KTRFAC_INHERIT; 142 break; 143 #ifdef KTRUSS 144 case 'l': 145 tail = 1; 146 break; 147 case 'm': 148 maxdata = atoi(optarg); 149 break; 150 case 'o': 151 outfile = optarg; 152 break; 153 #endif 154 case 'n': 155 block = 0; 156 break; 157 case 'p': 158 pid = rpid(optarg); 159 pidset = 1; 160 break; 161 #ifdef KTRUSS 162 case 'R': 163 timestamp = 2; /* relative timestamp */ 164 break; 165 #else 166 case 's': 167 synclog = 1; 168 break; 169 #endif 170 #ifdef KTRUSS 171 case 'T': 172 timestamp = 1; 173 break; 174 #endif 175 case 't': 176 trset = 1; 177 trpoints = getpoints(trpoints, optarg); 178 if (trpoints < 0) { 179 warnx("unknown facility in %s", optarg); 180 usage(); 181 } 182 break; 183 case 'v': 184 vers = atoi(optarg); 185 break; 186 default: 187 usage(); 188 } 189 argv += optind; 190 argc -= optind; 191 192 if (!trset) 193 trpoints |= clear == NOTSET ? DEF_POINTS : ALL_POINTS; 194 195 if ((pidset && *argv) || (!pidset && !*argv)) { 196 #ifdef KTRUSS 197 if (!infile) 198 #endif 199 usage(); 200 } 201 202 #ifdef KTRUSS 203 if (clear == CLEAR && outfile == NULL && pid == 0) 204 usage(); 205 206 if (infile) { 207 dumpfile(infile, 0, trpoints); 208 exit(0); 209 } 210 211 setemul(emul_name, 0, 0); 212 #endif 213 214 /* 215 * For cleaner traces, initialize malloc now rather 216 * than in a traced subprocess. 217 */ 218 free(malloc(1)); 219 220 (void)signal(SIGSYS, no_ktrace); 221 if (clear != NOTSET) { 222 if (clear == CLEARALL) { 223 ops = KTROP_CLEAR | KTRFLAG_DESCEND; 224 trpoints = ALL_POINTS; 225 pid = 1; 226 } else 227 ops |= pid ? KTROP_CLEAR : KTROP_CLEARFILE; 228 229 (void)do_ktrace(outfile, vers, ops, trpoints, pid, block); 230 exit(0); 231 } 232 233 if (outfile && strcmp(outfile, "-")) { 234 if ((fd = open(outfile, O_CREAT | O_WRONLY | 235 (append ? 0 : O_TRUNC) | (synclog ? 0 : O_SYNC), 236 DEFFILEMODE)) < 0) 237 err(EXIT_FAILURE, "%s", outfile); 238 (void)close(fd); 239 } 240 241 if (*argv) { 242 #ifdef KTRUSS 243 if (do_ktrace(outfile, vers, ops, trpoints, getpid(), block) == 1) { 244 execvp(argv[0], &argv[0]); 245 err(EXIT_FAILURE, "exec of '%s' failed", argv[0]); 246 } 247 #else 248 (void)do_ktrace(outfile, vers, ops, trpoints, getpid(), block); 249 execvp(argv[0], &argv[0]); 250 err(EXIT_FAILURE, "exec of '%s' failed", argv[0]); 251 #endif 252 } else 253 (void)do_ktrace(outfile, vers, ops, trpoints, pid, block); 254 return 0; 255 } 256 257 static int 258 rpid(char *p) 259 { 260 static int first; 261 262 if (first++) { 263 warnx("only one -g or -p flag is permitted."); 264 usage(); 265 } 266 if (!*p) { 267 warnx("illegal process id."); 268 usage(); 269 } 270 return (atoi(p)); 271 } 272 273 static void 274 fset(int fd, int flag) 275 { 276 int oflag = fcntl(fd, F_GETFL, 0); 277 278 if (oflag == -1) 279 err(EXIT_FAILURE, "Cannot get file flags"); 280 if (fcntl(fd, F_SETFL, oflag | flag) == -1) 281 err(EXIT_FAILURE, "Cannot set file flags"); 282 } 283 284 static void 285 fclear(int fd, int flag) 286 { 287 int oflag = fcntl(fd, F_GETFL, 0); 288 289 if (oflag == -1) 290 err(EXIT_FAILURE, "Cannot get file flags"); 291 if (fcntl(fd, F_SETFL, oflag & ~flag) == -1) 292 err(EXIT_FAILURE, "Cannot set file flags"); 293 } 294 295 static void 296 usage(void) 297 { 298 299 #define TRPOINTS "[AaceilmnSsuvw+-]" 300 #ifdef KTRUSS 301 (void)fprintf(stderr, "usage:\t%s " 302 "[-aCcdilnRT] [-e emulation] [-f infile] [-g pgrp] " 303 "[-m maxdata]\n\t " 304 "[-o outfile] [-p pid] [-t " TRPOINTS "]\n", getprogname()); 305 (void)fprintf(stderr, "\t%s " 306 "[-adinRT] [-e emulation] [-m maxdata] [-o outfile]\n\t " 307 "[-t " TRPOINTS "] [-v vers] command\n", 308 getprogname()); 309 #else 310 (void)fprintf(stderr, "usage:\t%s " 311 "[-aCcdins] [-f trfile] [-g pgrp] [-p pid] [-t " TRPOINTS "]\n", 312 getprogname()); 313 (void)fprintf(stderr, "\t%s " 314 "[-adis] [-f trfile] [-t " TRPOINTS "] command\n", 315 getprogname()); 316 #endif 317 exit(1); 318 } 319 320 static const char *ktracefile = NULL; 321 static void 322 /*ARGSUSED*/ 323 no_ktrace(int sig) 324 { 325 326 if (ktracefile) 327 (void)unlink(ktracefile); 328 (void)errx(EXIT_FAILURE, 329 "ktrace(2) system call not supported in the running" 330 " kernel; re-compile kernel with `options KTRACE'"); 331 } 332 333 static int 334 do_ktrace(const char *tracefile, int vers, int ops, int trpoints, int pid, 335 int block) 336 { 337 int ret; 338 ops |= vers << KTRFAC_VER_SHIFT; 339 340 if (KTROP(ops) == KTROP_SET && 341 (!tracefile || strcmp(tracefile, "-") == 0)) { 342 int pi[2], dofork; 343 344 if (pipe(pi) < 0) 345 err(EXIT_FAILURE, "pipe(2)"); 346 347 fset(pi[0], FD_CLOEXEC); 348 fset(pi[1], FD_CLOEXEC); 349 dofork = (pid == getpid()); 350 351 if (dofork) { 352 #ifdef KTRUSS 353 /* 354 * Create a child process and trace it. 355 */ 356 pid = fork(); 357 if (pid == -1) 358 err(EXIT_FAILURE, "fork"); 359 else if (pid == 0) { 360 pid = getpid(); 361 goto trace_and_exec; 362 } 363 #else 364 int fpid; 365 366 /* 367 * Create a dumper process and we will be 368 * traced. 369 */ 370 fpid = fork(); 371 if (fpid == -1) 372 err(EXIT_FAILURE, "fork"); 373 else if (fpid != 0) 374 goto trace_and_exec; 375 #endif 376 (void)close(pi[1]); 377 } else { 378 ret = fktrace(pi[1], ops, trpoints, pid); 379 if (ret == -1) 380 err(EXIT_FAILURE, "fd %d, pid %d", 381 pi[1], pid); 382 if (block) 383 fclear(pi[1], O_NONBLOCK); 384 } 385 #ifdef KTRUSS 386 dumpfile(NULL, pi[0], trpoints); 387 waitpid(pid, NULL, 0); 388 #else 389 { 390 char buf[BUFSIZ]; 391 int n; 392 393 while ((n = 394 read(pi[0], buf, sizeof(buf))) > 0) 395 if (write(STDOUT_FILENO, buf, (size_t)n) == -1) 396 warn("write failed"); 397 } 398 if (dofork) 399 _exit(0); 400 #endif 401 return 0; 402 403 trace_and_exec: 404 (void)close(pi[0]); 405 ret = fktrace(pi[1], ops, trpoints, pid); 406 if (ret == -1) 407 err(EXIT_FAILURE, "fd %d, pid %d", pi[1], pid); 408 if (block) 409 fclear(pi[1], O_NONBLOCK); 410 } else { 411 ret = ktrace(ktracefile = tracefile, ops, trpoints, pid); 412 if (ret == -1) 413 err(EXIT_FAILURE, "file %s, pid %d", 414 tracefile != NULL ? tracefile : "NULL", pid); 415 } 416 return 1; 417 } 418