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
40static 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
69static int rpid(char *);
70__dead static void usage(void);
71static int do_ktrace(const char *, int, int, int, int, int);
72__dead static void no_ktrace(int);
73static void fclear(int fd, int flag);
74
75#ifdef KTRUSS
76extern int timestamp, decimal, fancy, tail, maxdata;
77#endif
78
79int
80main(int argc, char *argv[])
81{
82	enum { NOTSET, CLEAR, CLEARALL } clear;
83	int block, append, ch, fd, trset, ops, pid, pidset, synclog, trpoints;
84	int vers;
85	const char *outfile;
86#ifdef KTRUSS
87	const char *infile;
88	const char *emul_name = "netbsd";
89#endif
90
91	clear = NOTSET;
92	append = ops = pidset = trset = synclog = 0;
93	trpoints = 0;
94	block = 1;
95	vers = 2;
96	pid = 0;	/* Appease GCC */
97
98#ifdef KTRUSS
99# define OPTIONS "aCce:df:g:ilm:no:p:RTt:v:"
100	outfile = infile = NULL;
101#else
102# define OPTIONS "aCcdf:g:ip:st:v:"
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		case 'v':
182			vers = atoi(optarg);
183			break;
184		default:
185			usage();
186		}
187	argv += optind;
188	argc -= optind;
189
190	if (!trset)
191		trpoints |= clear == NOTSET ? DEF_POINTS : ALL_POINTS;
192
193	if ((pidset && *argv) || (!pidset && !*argv)) {
194#ifdef KTRUSS
195		if (!infile)
196#endif
197			usage();
198	}
199
200#ifdef KTRUSS
201	if (clear == CLEAR && outfile == NULL && pid == 0)
202		usage();
203
204	if (infile) {
205		dumpfile(infile, 0, trpoints);
206		exit(0);
207	}
208
209	setemul(emul_name, 0, 0);
210#endif
211
212	/*
213	 * For cleaner traces, initialize malloc now rather
214	 * than in a traced subprocess.
215	 */
216	free(malloc(1));
217
218	(void)signal(SIGSYS, no_ktrace);
219	if (clear != NOTSET) {
220		if (clear == CLEARALL) {
221			ops = KTROP_CLEAR | KTRFLAG_DESCEND;
222			trpoints = ALL_POINTS;
223			pid = 1;
224		} else
225			ops |= pid ? KTROP_CLEAR : KTROP_CLEARFILE;
226
227		(void)do_ktrace(outfile, vers, ops, trpoints, pid, block);
228		exit(0);
229	}
230
231	if (outfile && strcmp(outfile, "-")) {
232		if ((fd = open(outfile, O_CREAT | O_WRONLY |
233		    (append ? 0 : O_TRUNC) | (synclog ? 0 : O_SYNC),
234		    DEFFILEMODE)) < 0)
235			err(EXIT_FAILURE, "%s", outfile);
236		(void)close(fd);
237	}
238
239	if (*argv) {
240#ifdef KTRUSS
241		if (do_ktrace(outfile, vers, ops, trpoints, getpid(), block) == 1) {
242			execvp(argv[0], &argv[0]);
243			err(EXIT_FAILURE, "exec of '%s' failed", argv[0]);
244		}
245#else
246		(void)do_ktrace(outfile, vers, ops, trpoints, getpid(), block);
247		execvp(argv[0], &argv[0]);
248		err(EXIT_FAILURE, "exec of '%s' failed", argv[0]);
249#endif
250	} else
251		(void)do_ktrace(outfile, vers, ops, trpoints, pid, block);
252	return 0;
253}
254
255static int
256rpid(char *p)
257{
258	static int first;
259
260	if (first++) {
261		warnx("only one -g or -p flag is permitted.");
262		usage();
263	}
264	if (!*p) {
265		warnx("illegal process id.");
266		usage();
267	}
268	return (atoi(p));
269}
270
271static void
272fclear(int fd, int flag)
273{
274	int oflag = fcntl(fd, F_GETFL, 0);
275
276	if (oflag == -1)
277		err(EXIT_FAILURE, "Cannot get file flags");
278	if (fcntl(fd, F_SETFL, oflag & ~flag) == -1)
279		err(EXIT_FAILURE, "Cannot set file flags");
280}
281
282static void
283usage(void)
284{
285
286#define	TRPOINTS "[AaceilmnSsuvw+-]"
287#ifdef KTRUSS
288	(void)fprintf(stderr, "usage:\t%s "
289	    "[-aCcdilnRT] [-e emulation] [-f infile] [-g pgrp] "
290	    "[-m maxdata]\n\t    "
291	    "[-o outfile] [-p pid] [-t " TRPOINTS "]\n", getprogname());
292	(void)fprintf(stderr, "\t%s "
293	    "[-adinRT] [-e emulation] [-m maxdata] [-o outfile]\n\t    "
294	    "[-t " TRPOINTS "] [-v vers] command\n",
295	    getprogname());
296#else
297	(void)fprintf(stderr, "usage:\t%s "
298	    "[-aCcdins] [-f trfile] [-g pgrp] [-p pid] [-t " TRPOINTS "]\n",
299	    getprogname());
300	(void)fprintf(stderr, "\t%s "
301	    "[-adis] [-f trfile] [-t " TRPOINTS "] command\n",
302	    getprogname());
303#endif
304	exit(1);
305}
306
307static const char *ktracefile = NULL;
308static void
309/*ARGSUSED*/
310no_ktrace(int sig)
311{
312
313	if (ktracefile)
314		(void)unlink(ktracefile);
315	(void)errx(EXIT_FAILURE,
316	    "ktrace(2) system call not supported in the running"
317	    " kernel; re-compile kernel with `options KTRACE'");
318}
319
320static int
321do_ktrace(const char *tracefile, int vers, int ops, int trpoints, int pid,
322    int block)
323{
324	int ret;
325	ops |= vers << KTRFAC_VER_SHIFT;
326
327	if (KTROP(ops) == KTROP_SET &&
328	    (!tracefile || strcmp(tracefile, "-") == 0)) {
329		int pi[2], dofork;
330
331		if (pipe2(pi, O_CLOEXEC) == -1)
332			err(EXIT_FAILURE, "pipe(2)");
333
334		dofork = (pid == getpid());
335
336		if (dofork) {
337#ifdef KTRUSS
338			/*
339			 * Create a child process and trace it.
340			 */
341			pid = fork();
342			if (pid == -1)
343				err(EXIT_FAILURE, "fork");
344			else if (pid == 0) {
345				pid = getpid();
346				goto trace_and_exec;
347			}
348#else
349			int fpid;
350
351			/*
352			 * Create a dumper process and we will be
353			 * traced.
354			 */
355			fpid = fork();
356			if (fpid == -1)
357				err(EXIT_FAILURE, "fork");
358			else if (fpid != 0)
359				goto trace_and_exec;
360#endif
361			(void)close(pi[1]);
362		} else {
363			ret = fktrace(pi[1], ops, trpoints, pid);
364			if (ret == -1)
365				err(EXIT_FAILURE, "fd %d, pid %d",
366				    pi[1], pid);
367			if (block)
368				fclear(pi[1], O_NONBLOCK);
369		}
370#ifdef KTRUSS
371		dumpfile(NULL, pi[0], trpoints);
372		waitpid(pid, NULL, 0);
373#else
374		{
375			char	buf[BUFSIZ];
376			int	n;
377
378			while ((n =
379			    read(pi[0], buf, sizeof(buf))) > 0)
380				if (write(STDOUT_FILENO, buf, (size_t)n) == -1)
381					warn("write failed");
382		}
383		if (dofork)
384			_exit(0);
385#endif
386		return 0;
387
388trace_and_exec:
389		(void)close(pi[0]);
390		ret = fktrace(pi[1], ops, trpoints, pid);
391		if (ret == -1)
392			err(EXIT_FAILURE, "fd %d, pid %d", pi[1], pid);
393		if (block)
394			fclear(pi[1], O_NONBLOCK);
395	} else {
396		ret = ktrace(ktracefile = tracefile, ops, trpoints, pid);
397		if (ret == -1)
398			err(EXIT_FAILURE, "file %s, pid %d",
399			    tracefile != NULL ? tracefile : "NULL", pid);
400	}
401	return 1;
402}
403