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