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