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