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