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