xref: /netbsd/usr.bin/ktrace/ktrace.c (revision 6550d01e)
1 /*	$NetBSD: ktrace.c,v 1.44 2011/02/05 15:22:17 yamt 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.44 2011/02/05 15:22:17 yamt 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 int	main(int, char *[]);
70 static int rpid(char *);
71 static void usage(void);
72 static int do_ktrace(const char *, int, int, int, int, int);
73 static void no_ktrace(int);
74 static void fset(int fd, int flag);
75 static void fclear(int fd, int flag);
76 
77 #ifdef KTRUSS
78 extern int timestamp, decimal, fancy, tail, maxdata;
79 #endif
80 
81 int
82 main(int argc, char *argv[])
83 {
84 	enum { NOTSET, CLEAR, CLEARALL } clear;
85 	int block, append, ch, fd, trset, ops, pid, pidset, synclog, trpoints;
86 	int vers;
87 	const char *outfile;
88 #ifdef KTRUSS
89 	const char *infile;
90 	const char *emul_name = "netbsd";
91 #endif
92 
93 	clear = NOTSET;
94 	append = ops = pidset = trset = synclog = 0;
95 	trpoints = 0;
96 	block = 1;
97 	vers = 2;
98 	pid = 0;	/* Appease GCC */
99 
100 #ifdef KTRUSS
101 # define OPTIONS "aCce:df:g:ilm:no:p:RTt:v:"
102 	outfile = infile = NULL;
103 #else
104 # define OPTIONS "aCcdf:g:ip:st:v:"
105 	outfile = DEF_TRACEFILE;
106 #endif
107 
108 	while ((ch = getopt(argc, argv, OPTIONS)) != -1)
109 		switch (ch) {
110 		case 'a':
111 			append = 1;
112 			break;
113 		case 'C':
114 			clear = CLEARALL;
115 			pidset = 1;
116 			break;
117 		case 'c':
118 			clear = CLEAR;
119 			pidset = 1;
120 			break;
121 		case 'd':
122 			ops |= KTRFLAG_DESCEND;
123 			break;
124 #ifdef KTRUSS
125 		case 'e':
126 			emul_name = strdup(optarg); /* it's safer to copy it */
127 			break;
128 		case 'f':
129 			infile = optarg;
130 			break;
131 #else
132 		case 'f':
133 			outfile = optarg;
134 			break;
135 #endif
136 		case 'g':
137 			pid = -rpid(optarg);
138 			pidset = 1;
139 			break;
140 		case 'i':
141 			trpoints |= KTRFAC_INHERIT;
142 			break;
143 #ifdef KTRUSS
144 		case 'l':
145 			tail = 1;
146 			break;
147 		case 'm':
148 			maxdata = atoi(optarg);
149 			break;
150 		case 'o':
151 			outfile = optarg;
152 			break;
153 #endif
154 		case 'n':
155 			block = 0;
156 			break;
157 		case 'p':
158 			pid = rpid(optarg);
159 			pidset = 1;
160 			break;
161 #ifdef KTRUSS
162 		case 'R':
163 			timestamp = 2;	/* relative timestamp */
164 			break;
165 #else
166 		case 's':
167 			synclog = 1;
168 			break;
169 #endif
170 #ifdef KTRUSS
171 		case 'T':
172 			timestamp = 1;
173 			break;
174 #endif
175 		case 't':
176 			trset = 1;
177 			trpoints = getpoints(trpoints, optarg);
178 			if (trpoints < 0) {
179 				warnx("unknown facility in %s", optarg);
180 				usage();
181 			}
182 			break;
183 		case 'v':
184 			vers = atoi(optarg);
185 			break;
186 		default:
187 			usage();
188 		}
189 	argv += optind;
190 	argc -= optind;
191 
192 	if (!trset)
193 		trpoints |= clear == NOTSET ? DEF_POINTS : ALL_POINTS;
194 
195 	if ((pidset && *argv) || (!pidset && !*argv)) {
196 #ifdef KTRUSS
197 		if (!infile)
198 #endif
199 			usage();
200 	}
201 
202 #ifdef KTRUSS
203 	if (clear == CLEAR && outfile == NULL && pid == 0)
204 		usage();
205 
206 	if (infile) {
207 		dumpfile(infile, 0, trpoints);
208 		exit(0);
209 	}
210 
211 	setemul(emul_name, 0, 0);
212 #endif
213 
214 	/*
215 	 * For cleaner traces, initialize malloc now rather
216 	 * than in a traced subprocess.
217 	 */
218 	free(malloc(1));
219 
220 	(void)signal(SIGSYS, no_ktrace);
221 	if (clear != NOTSET) {
222 		if (clear == CLEARALL) {
223 			ops = KTROP_CLEAR | KTRFLAG_DESCEND;
224 			trpoints = ALL_POINTS;
225 			pid = 1;
226 		} else
227 			ops |= pid ? KTROP_CLEAR : KTROP_CLEARFILE;
228 
229 		(void)do_ktrace(outfile, vers, ops, trpoints, pid, block);
230 		exit(0);
231 	}
232 
233 	if (outfile && strcmp(outfile, "-")) {
234 		if ((fd = open(outfile, O_CREAT | O_WRONLY |
235 		    (append ? 0 : O_TRUNC) | (synclog ? 0 : O_SYNC),
236 		    DEFFILEMODE)) < 0)
237 			err(EXIT_FAILURE, "%s", outfile);
238 		(void)close(fd);
239 	}
240 
241 	if (*argv) {
242 #ifdef KTRUSS
243 		if (do_ktrace(outfile, vers, ops, trpoints, getpid(), block) == 1) {
244 			execvp(argv[0], &argv[0]);
245 			err(EXIT_FAILURE, "exec of '%s' failed", argv[0]);
246 		}
247 #else
248 		(void)do_ktrace(outfile, vers, ops, trpoints, getpid(), block);
249 		execvp(argv[0], &argv[0]);
250 		err(EXIT_FAILURE, "exec of '%s' failed", argv[0]);
251 #endif
252 	} else
253 		(void)do_ktrace(outfile, vers, ops, trpoints, pid, block);
254 	return 0;
255 }
256 
257 static int
258 rpid(char *p)
259 {
260 	static int first;
261 
262 	if (first++) {
263 		warnx("only one -g or -p flag is permitted.");
264 		usage();
265 	}
266 	if (!*p) {
267 		warnx("illegal process id.");
268 		usage();
269 	}
270 	return (atoi(p));
271 }
272 
273 static void
274 fset(int fd, int flag)
275 {
276 	int oflag = fcntl(fd, F_GETFL, 0);
277 
278 	if (oflag == -1)
279 		err(EXIT_FAILURE, "Cannot get file flags");
280 	if (fcntl(fd, F_SETFL, oflag | flag) == -1)
281 		err(EXIT_FAILURE, "Cannot set file flags");
282 }
283 
284 static void
285 fclear(int fd, int flag)
286 {
287 	int oflag = fcntl(fd, F_GETFL, 0);
288 
289 	if (oflag == -1)
290 		err(EXIT_FAILURE, "Cannot get file flags");
291 	if (fcntl(fd, F_SETFL, oflag & ~flag) == -1)
292 		err(EXIT_FAILURE, "Cannot set file flags");
293 }
294 
295 static void
296 usage(void)
297 {
298 
299 #define	TRPOINTS "[AaceilmnSsuvw+-]"
300 #ifdef KTRUSS
301 	(void)fprintf(stderr, "usage:\t%s "
302 	    "[-aCcdilnRT] [-e emulation] [-f infile] [-g pgrp] "
303 	    "[-m maxdata]\n\t    "
304 	    "[-o outfile] [-p pid] [-t " TRPOINTS "]\n", getprogname());
305 	(void)fprintf(stderr, "\t%s "
306 	    "[-adinRT] [-e emulation] [-m maxdata] [-o outfile]\n\t    "
307 	    "[-t " TRPOINTS "] [-v vers] command\n",
308 	    getprogname());
309 #else
310 	(void)fprintf(stderr, "usage:\t%s "
311 	    "[-aCcdins] [-f trfile] [-g pgrp] [-p pid] [-t " TRPOINTS "]\n",
312 	    getprogname());
313 	(void)fprintf(stderr, "\t%s "
314 	    "[-adis] [-f trfile] [-t " TRPOINTS "] command\n",
315 	    getprogname());
316 #endif
317 	exit(1);
318 }
319 
320 static const char *ktracefile = NULL;
321 static void
322 /*ARGSUSED*/
323 no_ktrace(int sig)
324 {
325 
326 	if (ktracefile)
327 		(void)unlink(ktracefile);
328 	(void)errx(EXIT_FAILURE,
329 	    "ktrace(2) system call not supported in the running"
330 	    " kernel; re-compile kernel with `options KTRACE'");
331 }
332 
333 static int
334 do_ktrace(const char *tracefile, int vers, int ops, int trpoints, int pid,
335     int block)
336 {
337 	int ret;
338 	ops |= vers << KTRFAC_VER_SHIFT;
339 
340 	if (KTROP(ops) == KTROP_SET &&
341 	    (!tracefile || strcmp(tracefile, "-") == 0)) {
342 		int pi[2], dofork;
343 
344 		if (pipe(pi) < 0)
345 			err(EXIT_FAILURE, "pipe(2)");
346 
347 		fset(pi[0], FD_CLOEXEC);
348 		fset(pi[1], FD_CLOEXEC);
349 		dofork = (pid == getpid());
350 
351 		if (dofork) {
352 #ifdef KTRUSS
353 			/*
354 			 * Create a child process and trace it.
355 			 */
356 			pid = fork();
357 			if (pid == -1)
358 				err(EXIT_FAILURE, "fork");
359 			else if (pid == 0) {
360 				pid = getpid();
361 				goto trace_and_exec;
362 			}
363 #else
364 			int fpid;
365 
366 			/*
367 			 * Create a dumper process and we will be
368 			 * traced.
369 			 */
370 			fpid = fork();
371 			if (fpid == -1)
372 				err(EXIT_FAILURE, "fork");
373 			else if (fpid != 0)
374 				goto trace_and_exec;
375 #endif
376 			(void)close(pi[1]);
377 		} else {
378 			ret = fktrace(pi[1], ops, trpoints, pid);
379 			if (ret == -1)
380 				err(EXIT_FAILURE, "fd %d, pid %d",
381 				    pi[1], pid);
382 			if (block)
383 				fclear(pi[1], O_NONBLOCK);
384 		}
385 #ifdef KTRUSS
386 		dumpfile(NULL, pi[0], trpoints);
387 		waitpid(pid, NULL, 0);
388 #else
389 		{
390 			char	buf[BUFSIZ];
391 			int	n;
392 
393 			while ((n =
394 			    read(pi[0], buf, sizeof(buf))) > 0)
395 				if (write(STDOUT_FILENO, buf, (size_t)n) == -1)
396 					warn("write failed");
397 		}
398 		if (dofork)
399 			_exit(0);
400 #endif
401 		return 0;
402 
403 trace_and_exec:
404 		(void)close(pi[0]);
405 		ret = fktrace(pi[1], ops, trpoints, pid);
406 		if (ret == -1)
407 			err(EXIT_FAILURE, "fd %d, pid %d", pi[1], pid);
408 		if (block)
409 			fclear(pi[1], O_NONBLOCK);
410 	} else {
411 		ret = ktrace(ktracefile = tracefile, ops, trpoints, pid);
412 		if (ret == -1)
413 			err(EXIT_FAILURE, "file %s, pid %d",
414 			    tracefile != NULL ? tracefile : "NULL", pid);
415 	}
416 	return 1;
417 }
418