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