xref: /netbsd-src/usr.bin/ktrace/ktrace.c (revision 4cde7eb37ebdcbcb28c1d0d93abf2425854c964a)
1 /*	$NetBSD: ktrace.c,v 1.46 2013/01/24 17:47:58 christos 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.46 2013/01/24 17:47:58 christos 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 fclear(int fd, int flag);
74 
75 #ifdef KTRUSS
76 extern int timestamp, decimal, fancy, tail, maxdata;
77 #endif
78 
79 int
main(int argc,char * argv[])80 main(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 
255 static int
rpid(char * p)256 rpid(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 
271 static void
fclear(int fd,int flag)272 fclear(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 
282 static void
usage(void)283 usage(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 
307 static const char *ktracefile = NULL;
308 static void
309 /*ARGSUSED*/
no_ktrace(int sig)310 no_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 
320 static int
do_ktrace(const char * tracefile,int vers,int ops,int trpoints,int pid,int block)321 do_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 
388 trace_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