ktrace.c revision 1.45 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