Home | History | Annotate | Line # | Download | only in ktrace
ktrace.c revision 1.45
      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