11590Srgrimes/*-
21590Srgrimes * Copyright (c) 1988, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
301590Srgrimes#ifndef lint
3193523Sdwmalonestatic const char copyright[] =
321590Srgrimes"@(#) Copyright (c) 1988, 1993\n\
331590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
341590Srgrimes#endif /* not lint */
351590Srgrimes
36110401Scharnier#if 0
371590Srgrimes#ifndef lint
381590Srgrimesstatic char sccsid[] = "@(#)ktrace.c	8.1 (Berkeley) 6/6/93";
39110401Scharnier#endif /* not lint */
4016509Sjraynard#endif
41110401Scharnier
4293523Sdwmalone#include <sys/cdefs.h>
4393523Sdwmalone__FBSDID("$FreeBSD$");
4493523Sdwmalone
451590Srgrimes#include <sys/param.h>
46226504Sdes#include <sys/file.h>
471590Srgrimes#include <sys/stat.h>
481590Srgrimes#include <sys/time.h>
491590Srgrimes#include <sys/uio.h>
501590Srgrimes#include <sys/ktrace.h>
5116509Sjraynard
5216509Sjraynard#include <err.h>
53226504Sdes#include <errno.h>
54226504Sdes#include <inttypes.h>
551590Srgrimes#include <stdio.h>
5693523Sdwmalone#include <stdlib.h>
5716509Sjraynard#include <unistd.h>
5816509Sjraynard
591590Srgrimes#include "ktrace.h"
601590Srgrimes
6195649Smarkmstatic char def_tracefile[] = DEF_TRACEFILE;
6295649Smarkm
63226504Sdesstatic enum clear { NOTSET, CLEAR, CLEARALL } clear = NOTSET;
64226504Sdesstatic int pid;
65226504Sdes
6693523Sdwmalonestatic void no_ktrace(int);
67226504Sdesstatic void set_pid_clear(const char *, enum clear);
6893523Sdwmalonestatic void usage(void);
692354Sats
7093523Sdwmaloneint
7195649Smarkmmain(int argc, char *argv[])
721590Srgrimes{
73226504Sdes	int append, ch, fd, inherit, ops, trpoints;
7493523Sdwmalone	const char *tracefile;
7516844Sjoerg	mode_t omask;
7623894Sjoerg	struct stat sb;
771590Srgrimes
78226504Sdes	append = ops = inherit = 0;
791590Srgrimes	trpoints = DEF_POINTS;
8095649Smarkm	tracefile = def_tracefile;
8124360Simp	while ((ch = getopt(argc,argv,"aCcdf:g:ip:t:")) != -1)
821590Srgrimes		switch((char)ch) {
831590Srgrimes		case 'a':
841590Srgrimes			append = 1;
851590Srgrimes			break;
861590Srgrimes		case 'C':
87226504Sdes			set_pid_clear("1", CLEARALL);
881590Srgrimes			break;
891590Srgrimes		case 'c':
90226504Sdes			set_pid_clear(NULL, CLEAR);
911590Srgrimes			break;
921590Srgrimes		case 'd':
931590Srgrimes			ops |= KTRFLAG_DESCEND;
941590Srgrimes			break;
951590Srgrimes		case 'f':
961590Srgrimes			tracefile = optarg;
971590Srgrimes			break;
981590Srgrimes		case 'g':
99226504Sdes			set_pid_clear(optarg, NOTSET);
100226504Sdes			pid = -pid;
1011590Srgrimes			break;
1021590Srgrimes		case 'i':
1031590Srgrimes			inherit = 1;
1041590Srgrimes			break;
1051590Srgrimes		case 'p':
106226504Sdes			set_pid_clear(optarg, NOTSET);
1071590Srgrimes			break;
1081590Srgrimes		case 't':
1091590Srgrimes			trpoints = getpoints(optarg);
1101590Srgrimes			if (trpoints < 0) {
11116509Sjraynard				warnx("unknown facility in %s", optarg);
1121590Srgrimes				usage();
1131590Srgrimes			}
1141590Srgrimes			break;
1151590Srgrimes		default:
1161590Srgrimes			usage();
1171590Srgrimes		}
118226504Sdes
1191590Srgrimes	argv += optind;
1201590Srgrimes	argc -= optind;
121226504Sdes
122226504Sdes	/* must have either -[Cc], a pid or a command */
123226504Sdes	if (clear == NOTSET && pid == 0 && argc == 0)
1241590Srgrimes		usage();
125226504Sdes	/* can't have both a pid and a command */
126226504Sdes	/* (note that -C sets pid to 1) */
127226504Sdes	if (pid != 0 && argc > 0) {
128226504Sdes		usage();
129226504Sdes	}
130226504Sdes
1311590Srgrimes	if (inherit)
1321590Srgrimes		trpoints |= KTRFAC_INHERIT;
1331590Srgrimes
13416509Sjraynard	(void)signal(SIGSYS, no_ktrace);
1351590Srgrimes	if (clear != NOTSET) {
1361590Srgrimes		if (clear == CLEARALL) {
1371590Srgrimes			ops = KTROP_CLEAR | KTRFLAG_DESCEND;
1381590Srgrimes			trpoints = ALL_POINTS;
139226504Sdes		} else {
140226504Sdes			ops |= pid ? KTROP_CLEAR : KTROP_CLEARFILE;
141226504Sdes		}
1421590Srgrimes		if (ktrace(tracefile, ops, trpoints, pid) < 0)
14362894Skris			err(1, "%s", tracefile);
1441590Srgrimes		exit(0);
1451590Srgrimes	}
1461590Srgrimes
14716844Sjoerg	omask = umask(S_IRWXG|S_IRWXO);
14823894Sjoerg	if (append) {
149156715Srwatson		if ((fd = open(tracefile, O_CREAT | O_WRONLY | O_NONBLOCK,
150156715Srwatson		    DEFFILEMODE)) < 0)
15162894Skris			err(1, "%s", tracefile);
15223894Sjoerg		if (fstat(fd, &sb) != 0 || sb.st_uid != getuid())
153110401Scharnier			errx(1, "refuse to append to %s not owned by you",
15423894Sjoerg			    tracefile);
155156715Srwatson		if (!(S_ISREG(sb.st_mode)))
156156715Srwatson			errx(1, "%s not regular file", tracefile);
15723894Sjoerg	} else {
15823894Sjoerg		if (unlink(tracefile) == -1 && errno != ENOENT)
15923894Sjoerg			err(1, "unlink %s", tracefile);
16023894Sjoerg		if ((fd = open(tracefile, O_CREAT | O_EXCL | O_WRONLY,
16123894Sjoerg		    DEFFILEMODE)) < 0)
16262894Skris			err(1, "%s", tracefile);
16323894Sjoerg	}
16416844Sjoerg	(void)umask(omask);
1651590Srgrimes	(void)close(fd);
1661590Srgrimes
167219043Sdchagin	trpoints |= PROC_ABI_POINTS;
168219043Sdchagin
169226504Sdes	if (argc > 0) {
1701590Srgrimes		if (ktrace(tracefile, ops, trpoints, getpid()) < 0)
17162894Skris			err(1, "%s", tracefile);
172226504Sdes		execvp(*argv, argv);
173226504Sdes		err(1, "exec of '%s' failed", *argv);
1741590Srgrimes	}
175226504Sdes	if (ktrace(tracefile, ops, trpoints, pid) < 0)
17662894Skris		err(1, "%s", tracefile);
1771590Srgrimes	exit(0);
1781590Srgrimes}
1791590Srgrimes
180226504Sdesstatic void
181226504Sdesset_pid_clear(const char *p, enum clear cl)
1821590Srgrimes{
183226504Sdes	intmax_t n;
184226504Sdes	char *e;
1851590Srgrimes
186226504Sdes	if (clear != NOTSET && cl != NOTSET) {
187226504Sdes		/* either -c and -C or either of them twice */
188226504Sdes		warnx("only one -c or -C flag is permitted");
1891590Srgrimes		usage();
1901590Srgrimes	}
191226504Sdes	if ((clear == CLEARALL && p != NULL) || (cl == CLEARALL && pid != 0)) {
192226504Sdes		/* both -C and a pid or pgid */
193226504Sdes		warnx("the -C flag may not be combined with -g or -p");
1941590Srgrimes		usage();
1951590Srgrimes	}
196226504Sdes	if (p != NULL && pid != 0) {
197226504Sdes		/* either -p and -g or either of them twice */
198226504Sdes		warnx("only one -g or -p flag is permitted");
199226504Sdes		usage();
200226504Sdes	}
201226504Sdes	if (p != NULL) {
202226504Sdes		errno = 0;
203226504Sdes		n = strtoimax(p, &e, 10);
204226504Sdes		/*
205226504Sdes		 * 1) not a number, or outside the range of an intmax_t
206226504Sdes		 * 2) inside the range of intmax_t but outside the range
207226504Sdes		 *    of an int, keeping in mind that the pid may be
208226504Sdes		 *    negated if it's actually a pgid.
209226504Sdes		 */
210226504Sdes		if (*e != '\0' || n < 1 || errno == ERANGE ||
211226504Sdes		    n > (intmax_t)INT_MAX || n > -(intmax_t)INT_MIN) {
212226504Sdes			warnx("invalid process or group id");
213226504Sdes			usage();
214226504Sdes		}
215226504Sdes		pid = n;
216226504Sdes	}
217226504Sdes	if (cl != NOTSET)
218226504Sdes		if ((clear = cl) == CLEARALL)
219226504Sdes			pid = 1;
2201590Srgrimes}
2211590Srgrimes
22293523Sdwmalonestatic void
22395649Smarkmusage(void)
2241590Srgrimes{
225226504Sdes
226226504Sdes	fprintf(stderr, "%s\n%s\n",
227226504Sdes	    "usage: ktrace [-aCcdi] [-f trfile] [-g pgrp | -p pid] [-t trstr]",
228226504Sdes	    "       ktrace [-adi] [-f trfile] [-t trstr] command");
2291590Srgrimes	exit(1);
2301590Srgrimes}
23116509Sjraynard
23293523Sdwmalonestatic void
23395649Smarkmno_ktrace(int sig __unused)
23416509Sjraynard{
235226504Sdes
236226504Sdes	fprintf(stderr, "error:\t%s\n\t%s\n",
237226504Sdes	    "ktrace() system call not supported in the running kernel",
238226504Sdes	    "re-compile kernel with 'options KTRACE'");
23916509Sjraynard        exit(1);
24016509Sjraynard}
241