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