xref: /netbsd-src/usr.bin/ktrace/ktrace.c (revision b757af438b42b93f8c6571f026d8b8ef3eaf5fc9)
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