Deleted Added
sdiff udiff text old ( 286914 ) new ( 286938 )
full compact
1/*
2 * Copyright 1997 Sean Eric Fagan
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. All advertising materials mentioning features or use of this software
13 * must display the following acknowledgement:
14 * This product includes software developed by Sean Eric Fagan
15 * 4. Neither the name of the author may be used to endorse or promote
16 * products derived from this software without specific prior written
17 * permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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#ifndef lint
33static const char rcsid[] =
34 "$FreeBSD: head/usr.bin/truss/syscalls.c 286938 2015-08-19 20:02:03Z jhb $";
35#endif /* not lint */
36
37/*
38 * This file has routines used to print out system calls and their
39 * arguments.
40 */
41
42#include <sys/types.h>
43#include <sys/mman.h>
44#include <sys/procctl.h>
45#include <sys/ptrace.h>
46#include <sys/socket.h>
47#include <sys/time.h>
48#include <sys/un.h>
49#include <sys/wait.h>
50#include <netinet/in.h>
51#include <arpa/inet.h>
52#include <sys/ioccom.h>
53#include <machine/atomic.h>
54#include <errno.h>
55#include <sys/umtx.h>
56#include <sys/event.h>
57#include <sys/stat.h>
58#include <sys/resource.h>
59#include <machine/sysarch.h>
60
61#include <ctype.h>
62#include <err.h>
63#include <fcntl.h>
64#include <poll.h>
65#include <signal.h>
66#include <stdint.h>
67#include <stdio.h>
68#include <stdlib.h>
69#include <string.h>
70#include <time.h>
71#include <unistd.h>
72#include <vis.h>
73
74#include "truss.h"
75#include "extern.h"
76#include "syscall.h"
77
78/* 64-bit alignment on 32-bit platforms. */
79#ifdef __powerpc__
80#define QUAD_ALIGN 1
81#else
82#define QUAD_ALIGN 0
83#endif
84
85/* Number of slots needed for a 64-bit argument. */
86#ifdef __LP64__
87#define QUAD_SLOTS 1
88#else
89#define QUAD_SLOTS 2
90#endif
91
92/*
93 * This should probably be in its own file, sorted alphabetically.
94 */
95static struct syscall syscalls[] = {
96 { .name = "fcntl", .ret_type = 1, .nargs = 3,
97 .args = { { Int, 0 }, { Fcntl, 1 }, { Fcntlflag, 2 } } },
98 { .name = "fork", .ret_type = 1, .nargs = 0 },
99 { .name = "vfork", .ret_type = 1, .nargs = 0 },
100 { .name = "rfork", .ret_type = 1, .nargs = 1,
101 .args = { { Rforkflags, 0 } } },
102 { .name = "getegid", .ret_type = 1, .nargs = 0 },
103 { .name = "geteuid", .ret_type = 1, .nargs = 0 },
104 { .name = "linux_readlink", .ret_type = 1, .nargs = 3,
105 .args = { { Name, 0 }, { Name | OUT, 1 }, { Int, 2 } } },
106 { .name = "linux_socketcall", .ret_type = 1, .nargs = 2,
107 .args = { { Int, 0 }, { LinuxSockArgs, 1 } } },
108 { .name = "getgid", .ret_type = 1, .nargs = 0 },
109 { .name = "getpid", .ret_type = 1, .nargs = 0 },
110 { .name = "getpgid", .ret_type = 1, .nargs = 1,
111 .args = { { Int, 0 } } },
112 { .name = "getpgrp", .ret_type = 1, .nargs = 0 },
113 { .name = "getppid", .ret_type = 1, .nargs = 0 },
114 { .name = "getsid", .ret_type = 1, .nargs = 1,
115 .args = { { Int, 0 } } },
116 { .name = "getuid", .ret_type = 1, .nargs = 0 },
117 { .name = "issetugid", .ret_type = 1, .nargs = 0 },
118 { .name = "readlink", .ret_type = 1, .nargs = 3,
119 .args = { { Name, 0 }, { Readlinkres | OUT, 1 }, { Int, 2 } } },
120 { .name = "readlinkat", .ret_type = 1, .nargs = 4,
121 .args = { { Atfd, 0 }, { Name, 1 }, { Readlinkres | OUT, 2 },
122 { Int, 3 } } },
123 { .name = "lseek", .ret_type = 2, .nargs = 3,
124 .args = { { Int, 0 }, { Quad, 1 + QUAD_ALIGN },
125 { Whence, 1 + QUAD_SLOTS + QUAD_ALIGN } } },
126 { .name = "linux_lseek", .ret_type = 2, .nargs = 3,
127 .args = { { Int, 0 }, { Int, 1 }, { Whence, 2 } } },
128 { .name = "mmap", .ret_type = 2, .nargs = 6,
129 .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 }, { Mmapflags, 3 },
130 { Int, 4 }, { Quad, 5 + QUAD_ALIGN } } },
131 { .name = "linux_mkdir", .ret_type = 1, .nargs = 2,
132 .args = { { Name | IN, 0 }, { Int, 1 } } },
133 { .name = "mprotect", .ret_type = 1, .nargs = 3,
134 .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 } } },
135 { .name = "open", .ret_type = 1, .nargs = 3,
136 .args = { { Name | IN, 0 }, { Open, 1 }, { Octal, 2 } } },
137 { .name = "openat", .ret_type = 1, .nargs = 4,
138 .args = { { Atfd, 0 }, { Name | IN, 1 }, { Open, 2 },
139 { Octal, 3 } } },
140 { .name = "mkdir", .ret_type = 1, .nargs = 2,
141 .args = { { Name, 0 }, { Octal, 1 } } },
142 { .name = "mkdirat", .ret_type = 1, .nargs = 3,
143 .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } },
144 { .name = "linux_open", .ret_type = 1, .nargs = 3,
145 .args = { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } },
146 { .name = "close", .ret_type = 1, .nargs = 1,
147 .args = { { Int, 0 } } },
148 { .name = "link", .ret_type = 0, .nargs = 2,
149 .args = { { Name, 0 }, { Name, 1 } } },
150 { .name = "linkat", .ret_type = 0, .nargs = 5,
151 .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 },
152 { Atflags, 4 } } },
153 { .name = "unlink", .ret_type = 0, .nargs = 1,
154 .args = { { Name, 0 } } },
155 { .name = "unlinkat", .ret_type = 0, .nargs = 3,
156 .args = { { Atfd, 0 }, { Name, 1 }, { Atflags, 2 } } },
157 { .name = "chdir", .ret_type = 0, .nargs = 1,
158 .args = { { Name, 0 } } },
159 { .name = "chroot", .ret_type = 0, .nargs = 1,
160 .args = { { Name, 0 } } },
161 { .name = "mkfifo", .ret_type = 0, .nargs = 2,
162 .args = { { Name, 0 }, { Octal, 1 } } },
163 { .name = "mkfifoat", .ret_type = 0, .nargs = 3,
164 .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } },
165 { .name = "mknod", .ret_type = 0, .nargs = 3,
166 .args = { { Name, 0 }, { Octal, 1 }, { Int, 2 } } },
167 { .name = "mknodat", .ret_type = 0, .nargs = 4,
168 .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Int, 3 } } },
169 { .name = "chmod", .ret_type = 0, .nargs = 2,
170 .args = { { Name, 0 }, { Octal, 1 } } },
171 { .name = "fchmod", .ret_type = 0, .nargs = 2,
172 .args = { { Int, 0 }, { Octal, 1 } } },
173 { .name = "lchmod", .ret_type = 0, .nargs = 2,
174 .args = { { Name, 0 }, { Octal, 1 } } },
175 { .name = "fchmodat", .ret_type = 0, .nargs = 4,
176 .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Atflags, 3 } } },
177 { .name = "chown", .ret_type = 0, .nargs = 3,
178 .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } },
179 { .name = "fchown", .ret_type = 0, .nargs = 3,
180 .args = { { Int, 0 }, { Int, 1 }, { Int, 2 } } },
181 { .name = "lchown", .ret_type = 0, .nargs = 3,
182 .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } },
183 { .name = "fchownat", .ret_type = 0, .nargs = 5,
184 .args = { { Atfd, 0 }, { Name, 1 }, { Int, 2 }, { Int, 3 },
185 { Atflags, 4 } } },
186 { .name = "linux_stat64", .ret_type = 1, .nargs = 3,
187 .args = { { Name | IN, 0 }, { Ptr | OUT, 1 }, { Ptr | IN, 1 } } },
188 { .name = "mount", .ret_type = 0, .nargs = 4,
189 .args = { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 } } },
190 { .name = "umount", .ret_type = 0, .nargs = 2,
191 .args = { { Name, 0 }, { Int, 2 } } },
192 { .name = "fstat", .ret_type = 1, .nargs = 2,
193 .args = { { Int, 0 }, { Stat | OUT, 1 } } },
194 { .name = "fstatat", .ret_type = 1, .nargs = 4,
195 .args = { { Atfd, 0 }, { Name | IN, 1 }, { Stat | OUT, 2 },
196 { Atflags, 3 } } },
197 { .name = "stat", .ret_type = 1, .nargs = 2,
198 .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } },
199 { .name = "lstat", .ret_type = 1, .nargs = 2,
200 .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } },
201 { .name = "linux_newstat", .ret_type = 1, .nargs = 2,
202 .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } },
203 { .name = "linux_access", .ret_type = 1, .nargs = 2,
204 .args = { { Name, 0 }, { Accessmode, 1 } } },
205 { .name = "linux_newfstat", .ret_type = 1, .nargs = 2,
206 .args = { { Int, 0 }, { Ptr | OUT, 1 } } },
207 { .name = "write", .ret_type = 1, .nargs = 3,
208 .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 } } },
209 { .name = "ioctl", .ret_type = 1, .nargs = 3,
210 .args = { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 } } },
211 { .name = "break", .ret_type = 1, .nargs = 1,
212 .args = { { Ptr, 0 } } },
213 { .name = "exit", .ret_type = 0, .nargs = 1,
214 .args = { { Hex, 0 } } },
215 { .name = "access", .ret_type = 1, .nargs = 2,
216 .args = { { Name | IN, 0 }, { Accessmode, 1 } } },
217 { .name = "eaccess", .ret_type = 1, .nargs = 2,
218 .args = { { Name | IN, 0 }, { Accessmode, 1 } } },
219 { .name = "faccessat", .ret_type = 1, .nargs = 4,
220 .args = { { Atfd, 0 }, { Name | IN, 1 }, { Accessmode, 2 },
221 { Atflags, 3 } } },
222 { .name = "sigaction", .ret_type = 1, .nargs = 3,
223 .args = { { Signal, 0 }, { Sigaction | IN, 1 },
224 { Sigaction | OUT, 2 } } },
225 { .name = "accept", .ret_type = 1, .nargs = 3,
226 .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
227 { .name = "bind", .ret_type = 1, .nargs = 3,
228 .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
229 { .name = "bindat", .ret_type = 1, .nargs = 4,
230 .args = { { Atfd, 0 }, { Int, 1 }, { Sockaddr | IN, 2 },
231 { Int, 3 } } },
232 { .name = "connect", .ret_type = 1, .nargs = 3,
233 .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
234 { .name = "connectat", .ret_type = 1, .nargs = 4,
235 .args = { { Atfd, 0 }, { Int, 1 }, { Sockaddr | IN, 2 },
236 { Int, 3 } } },
237 { .name = "getpeername", .ret_type = 1, .nargs = 3,
238 .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
239 { .name = "getsockname", .ret_type = 1, .nargs = 3,
240 .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
241 { .name = "recvfrom", .ret_type = 1, .nargs = 6,
242 .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 },
243 { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } },
244 { .name = "sendto", .ret_type = 1, .nargs = 6,
245 .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 },
246 { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } },
247 { .name = "execve", .ret_type = 1, .nargs = 3,
248 .args = { { Name | IN, 0 }, { StringArray | IN, 1 },
249 { StringArray | IN, 2 } } },
250 { .name = "linux_execve", .ret_type = 1, .nargs = 3,
251 .args = { { Name | IN, 0 }, { StringArray | IN, 1 },
252 { StringArray | IN, 2 } } },
253 { .name = "kldload", .ret_type = 0, .nargs = 1,
254 .args = { { Name | IN, 0 } } },
255 { .name = "kldunload", .ret_type = 0, .nargs = 1,
256 .args = { { Int, 0 } } },
257 { .name = "kldfind", .ret_type = 0, .nargs = 1,
258 .args = { { Name | IN, 0 } } },
259 { .name = "kldnext", .ret_type = 0, .nargs = 1,
260 .args = { { Int, 0 } } },
261 { .name = "kldstat", .ret_type = 0, .nargs = 2,
262 .args = { { Int, 0 }, { Ptr, 1 } } },
263 { .name = "kldfirstmod", .ret_type = 0, .nargs = 1,
264 .args = { { Int, 0 } } },
265 { .name = "nanosleep", .ret_type = 0, .nargs = 1,
266 .args = { { Timespec, 0 } } },
267 { .name = "select", .ret_type = 1, .nargs = 5,
268 .args = { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 },
269 { Timeval, 4 } } },
270 { .name = "poll", .ret_type = 1, .nargs = 3,
271 .args = { { Pollfd, 0 }, { Int, 1 }, { Int, 2 } } },
272 { .name = "gettimeofday", .ret_type = 1, .nargs = 2,
273 .args = { { Timeval | OUT, 0 }, { Ptr, 1 } } },
274 { .name = "clock_gettime", .ret_type = 1, .nargs = 2,
275 .args = { { Int, 0 }, { Timespec | OUT, 1 } } },
276 { .name = "getitimer", .ret_type = 1, .nargs = 2,
277 .args = { { Int, 0 }, { Itimerval | OUT, 2 } } },
278 { .name = "setitimer", .ret_type = 1, .nargs = 3,
279 .args = { { Int, 0 }, { Itimerval, 1 }, { Itimerval | OUT, 2 } } },
280 { .name = "kse_release", .ret_type = 0, .nargs = 1,
281 .args = { { Timespec, 0 } } },
282 { .name = "kevent", .ret_type = 0, .nargs = 6,
283 .args = { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 },
284 { Int, 4 }, { Timespec, 5 } } },
285 { .name = "sigpending", .ret_type = 0, .nargs = 1,
286 .args = { { Sigset | OUT, 0 } } },
287 { .name = "sigprocmask", .ret_type = 0, .nargs = 3,
288 .args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } },
289 { .name = "sigqueue", .ret_type = 0, .nargs = 3,
290 .args = { { Int, 0 }, { Signal, 1 }, { LongHex, 2 } } },
291 { .name = "sigreturn", .ret_type = 0, .nargs = 1,
292 .args = { { Ptr, 0 } } },
293 { .name = "sigsuspend", .ret_type = 0, .nargs = 1,
294 .args = { { Sigset | IN, 0 } } },
295 { .name = "sigtimedwait", .ret_type = 1, .nargs = 3,
296 .args = { { Sigset | IN, 0 }, { Ptr, 1 }, { Timespec | IN, 2 } } },
297 { .name = "sigwait", .ret_type = 1, .nargs = 2,
298 .args = { { Sigset | IN, 0 }, { Ptr, 1 } } },
299 { .name = "sigwaitinfo", .ret_type = 1, .nargs = 2,
300 .args = { { Sigset | IN, 0 }, { Ptr, 1 } } },
301 { .name = "unmount", .ret_type = 1, .nargs = 2,
302 .args = { { Name, 0 }, { Int, 1 } } },
303 { .name = "socket", .ret_type = 1, .nargs = 3,
304 .args = { { Sockdomain, 0 }, { Socktype, 1 }, { Int, 2 } } },
305 { .name = "getrusage", .ret_type = 1, .nargs = 2,
306 .args = { { Int, 0 }, { Rusage | OUT, 1 } } },
307 { .name = "__getcwd", .ret_type = 1, .nargs = 2,
308 .args = { { Name | OUT, 0 }, { Int, 1 } } },
309 { .name = "shutdown", .ret_type = 1, .nargs = 2,
310 .args = { { Int, 0 }, { Shutdown, 1 } } },
311 { .name = "getrlimit", .ret_type = 1, .nargs = 2,
312 .args = { { Resource, 0 }, { Rlimit | OUT, 1 } } },
313 { .name = "setrlimit", .ret_type = 1, .nargs = 2,
314 .args = { { Resource, 0 }, { Rlimit | IN, 1 } } },
315 { .name = "utimes", .ret_type = 1, .nargs = 2,
316 .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } },
317 { .name = "lutimes", .ret_type = 1, .nargs = 2,
318 .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } },
319 { .name = "futimes", .ret_type = 1, .nargs = 2,
320 .args = { { Int, 0 }, { Timeval2 | IN, 1 } } },
321 { .name = "futimesat", .ret_type = 1, .nargs = 3,
322 .args = { { Atfd, 0 }, { Name | IN, 1 }, { Timeval2 | IN, 2 } } },
323 { .name = "futimens", .ret_type = 1, .nargs = 2,
324 .args = { { Int, 0 }, { Timespec2 | IN, 1 } } },
325 { .name = "utimensat", .ret_type = 1, .nargs = 4,
326 .args = { { Atfd, 0 }, { Name | IN, 1 }, { Timespec2 | IN, 2 },
327 { Atflags, 3 } } },
328 { .name = "chflags", .ret_type = 1, .nargs = 2,
329 .args = { { Name | IN, 0 }, { Hex, 1 } } },
330 { .name = "lchflags", .ret_type = 1, .nargs = 2,
331 .args = { { Name | IN, 0 }, { Hex, 1 } } },
332 { .name = "pathconf", .ret_type = 1, .nargs = 2,
333 .args = { { Name | IN, 0 }, { Pathconf, 1 } } },
334 { .name = "pipe", .ret_type = 1, .nargs = 1,
335 .args = { { Ptr, 0 } } },
336 { .name = "pipe2", .ret_type = 1, .nargs = 2,
337 .args = { { Ptr, 0 }, { Open, 1 } } },
338 { .name = "truncate", .ret_type = 1, .nargs = 3,
339 .args = { { Name | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } },
340 { .name = "ftruncate", .ret_type = 1, .nargs = 3,
341 .args = { { Int | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } },
342 { .name = "kill", .ret_type = 1, .nargs = 2,
343 .args = { { Int | IN, 0 }, { Signal | IN, 1 } } },
344 { .name = "munmap", .ret_type = 1, .nargs = 2,
345 .args = { { Ptr, 0 }, { Int, 1 } } },
346 { .name = "read", .ret_type = 1, .nargs = 3,
347 .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 } } },
348 { .name = "rename", .ret_type = 1, .nargs = 2,
349 .args = { { Name, 0 }, { Name, 1 } } },
350 { .name = "renameat", .ret_type = 1, .nargs = 4,
351 .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 } } },
352 { .name = "symlink", .ret_type = 1, .nargs = 2,
353 .args = { { Name, 0 }, { Name, 1 } } },
354 { .name = "symlinkat", .ret_type = 1, .nargs = 3,
355 .args = { { Name, 0 }, { Atfd, 1 }, { Name, 2 } } },
356 { .name = "posix_openpt", .ret_type = 1, .nargs = 1,
357 .args = { { Open, 0 } } },
358 { .name = "wait4", .ret_type = 1, .nargs = 4,
359 .args = { { Int, 0 }, { ExitStatus | OUT, 1 }, { Waitoptions, 2 },
360 { Rusage | OUT, 3 } } },
361 { .name = "wait6", .ret_type = 1, .nargs = 6,
362 .args = { { Idtype, 0 }, { Int, 1 }, { ExitStatus | OUT, 2 },
363 { Waitoptions, 3 }, { Rusage | OUT, 4 }, { Ptr, 5 } } },
364 { .name = "procctl", .ret_type = 1, .nargs = 4,
365 .args = { { Idtype, 0 }, { Int, 1 }, { Procctl, 2 }, { Ptr, 3 } } },
366 { .name = "sysarch", .ret_type = 1, .nargs = 2,
367 .args = { { Sysarch, 0 }, { Ptr, 1 } } },
368 { .name = "_umtx_op", .ret_type = 1, .nargs = 5,
369 .args = { { Ptr, 0 }, { Umtxop, 1 }, { LongHex, 2 }, { Ptr, 3 },
370 { Ptr, 4 } } },
371 { .name = "thr_kill", .ret_type = 0, .nargs = 2,
372 .args = { { Long, 0 }, { Signal, 1 } } },
373 { .name = "thr_self", .ret_type = 0, .nargs = 1,
374 .args = { { Ptr, 0 } } },
375 { .name = 0 },
376};
377
378/* Xlat idea taken from strace */
379struct xlat {
380 int val;
381 const char *str;
382};
383
384#define X(a) { a, #a },
385#define XEND { 0, NULL }
386
387static struct xlat kevent_filters[] = {
388 X(EVFILT_READ) X(EVFILT_WRITE) X(EVFILT_AIO) X(EVFILT_VNODE)
389 X(EVFILT_PROC) X(EVFILT_SIGNAL) X(EVFILT_TIMER)
390 X(EVFILT_PROCDESC) X(EVFILT_FS) X(EVFILT_LIO) X(EVFILT_USER)
391 X(EVFILT_SENDFILE) XEND
392};
393
394static struct xlat kevent_flags[] = {
395 X(EV_ADD) X(EV_DELETE) X(EV_ENABLE) X(EV_DISABLE) X(EV_ONESHOT)
396 X(EV_CLEAR) X(EV_RECEIPT) X(EV_DISPATCH) X(EV_FORCEONESHOT)
397 X(EV_DROP) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND
398};
399
400static struct xlat kevent_user_ffctrl[] = {
401 X(NOTE_FFNOP) X(NOTE_FFAND) X(NOTE_FFOR) X(NOTE_FFCOPY)
402 XEND
403};
404
405static struct xlat kevent_rdwr_fflags[] = {
406 X(NOTE_LOWAT) X(NOTE_FILE_POLL) XEND
407};
408
409static struct xlat kevent_vnode_fflags[] = {
410 X(NOTE_DELETE) X(NOTE_WRITE) X(NOTE_EXTEND) X(NOTE_ATTRIB)
411 X(NOTE_LINK) X(NOTE_RENAME) X(NOTE_REVOKE) XEND
412};
413
414static struct xlat kevent_proc_fflags[] = {
415 X(NOTE_EXIT) X(NOTE_FORK) X(NOTE_EXEC) X(NOTE_TRACK) X(NOTE_TRACKERR)
416 X(NOTE_CHILD) XEND
417};
418
419static struct xlat kevent_timer_fflags[] = {
420 X(NOTE_SECONDS) X(NOTE_MSECONDS) X(NOTE_USECONDS) X(NOTE_NSECONDS)
421 XEND
422};
423
424static struct xlat poll_flags[] = {
425 X(POLLSTANDARD) X(POLLIN) X(POLLPRI) X(POLLOUT) X(POLLERR)
426 X(POLLHUP) X(POLLNVAL) X(POLLRDNORM) X(POLLRDBAND)
427 X(POLLWRBAND) X(POLLINIGNEOF) XEND
428};
429
430static struct xlat mmap_flags[] = {
431 X(MAP_SHARED) X(MAP_PRIVATE) X(MAP_FIXED) X(MAP_RESERVED0020)
432 X(MAP_RESERVED0040) X(MAP_RESERVED0080) X(MAP_RESERVED0100)
433 X(MAP_HASSEMAPHORE) X(MAP_STACK) X(MAP_NOSYNC) X(MAP_ANON)
434 X(MAP_EXCL) X(MAP_NOCORE) X(MAP_PREFAULT_READ)
435#ifdef MAP_32BIT
436 X(MAP_32BIT)
437#endif
438 XEND
439};
440
441static struct xlat mprot_flags[] = {
442 X(PROT_NONE) X(PROT_READ) X(PROT_WRITE) X(PROT_EXEC) XEND
443};
444
445static struct xlat whence_arg[] = {
446 X(SEEK_SET) X(SEEK_CUR) X(SEEK_END) X(SEEK_DATA) X(SEEK_HOLE) XEND
447};
448
449static struct xlat sigaction_flags[] = {
450 X(SA_ONSTACK) X(SA_RESTART) X(SA_RESETHAND) X(SA_NOCLDSTOP)
451 X(SA_NODEFER) X(SA_NOCLDWAIT) X(SA_SIGINFO) XEND
452};
453
454static struct xlat fcntl_arg[] = {
455 X(F_DUPFD) X(F_GETFD) X(F_SETFD) X(F_GETFL) X(F_SETFL)
456 X(F_GETOWN) X(F_SETOWN) X(F_OGETLK) X(F_OSETLK) X(F_OSETLKW)
457 X(F_DUP2FD) X(F_GETLK) X(F_SETLK) X(F_SETLKW) X(F_SETLK_REMOTE)
458 X(F_READAHEAD) X(F_RDAHEAD) X(F_DUPFD_CLOEXEC) X(F_DUP2FD_CLOEXEC)
459 XEND
460};
461
462static struct xlat fcntlfd_arg[] = {
463 X(FD_CLOEXEC) XEND
464};
465
466static struct xlat fcntlfl_arg[] = {
467 X(O_APPEND) X(O_ASYNC) X(O_FSYNC) X(O_NONBLOCK) X(O_NOFOLLOW)
468 X(FRDAHEAD) X(O_DIRECT) XEND
469};
470
471static struct xlat sockdomain_arg[] = {
472 X(PF_UNSPEC) X(PF_LOCAL) X(PF_UNIX) X(PF_INET) X(PF_IMPLINK)
473 X(PF_PUP) X(PF_CHAOS) X(PF_NETBIOS) X(PF_ISO) X(PF_OSI)
474 X(PF_ECMA) X(PF_DATAKIT) X(PF_CCITT) X(PF_SNA) X(PF_DECnet)
475 X(PF_DLI) X(PF_LAT) X(PF_HYLINK) X(PF_APPLETALK) X(PF_ROUTE)
476 X(PF_LINK) X(PF_XTP) X(PF_COIP) X(PF_CNT) X(PF_SIP) X(PF_IPX)
477 X(PF_RTIP) X(PF_PIP) X(PF_ISDN) X(PF_KEY) X(PF_INET6)
478 X(PF_NATM) X(PF_ATM) X(PF_NETGRAPH) X(PF_SLOW) X(PF_SCLUSTER)
479 X(PF_ARP) X(PF_BLUETOOTH) X(PF_IEEE80211) X(PF_INET_SDP)
480 X(PF_INET6_SDP) XEND
481};
482
483static struct xlat socktype_arg[] = {
484 X(SOCK_STREAM) X(SOCK_DGRAM) X(SOCK_RAW) X(SOCK_RDM)
485 X(SOCK_SEQPACKET) XEND
486};
487
488static struct xlat open_flags[] = {
489 X(O_RDONLY) X(O_WRONLY) X(O_RDWR) X(O_ACCMODE) X(O_NONBLOCK)
490 X(O_APPEND) X(O_SHLOCK) X(O_EXLOCK) X(O_ASYNC) X(O_FSYNC)
491 X(O_NOFOLLOW) X(O_CREAT) X(O_TRUNC) X(O_EXCL) X(O_NOCTTY)
492 X(O_DIRECT) X(O_DIRECTORY) X(O_EXEC) X(O_TTY_INIT) X(O_CLOEXEC)
493 X(O_VERIFY) XEND
494};
495
496static struct xlat shutdown_arg[] = {
497 X(SHUT_RD) X(SHUT_WR) X(SHUT_RDWR) XEND
498};
499
500static struct xlat resource_arg[] = {
501 X(RLIMIT_CPU) X(RLIMIT_FSIZE) X(RLIMIT_DATA) X(RLIMIT_STACK)
502 X(RLIMIT_CORE) X(RLIMIT_RSS) X(RLIMIT_MEMLOCK) X(RLIMIT_NPROC)
503 X(RLIMIT_NOFILE) X(RLIMIT_SBSIZE) X(RLIMIT_VMEM) X(RLIMIT_NPTS)
504 X(RLIMIT_SWAP) X(RLIMIT_KQUEUES) XEND
505};
506
507static struct xlat pathconf_arg[] = {
508 X(_PC_LINK_MAX) X(_PC_MAX_CANON) X(_PC_MAX_INPUT)
509 X(_PC_NAME_MAX) X(_PC_PATH_MAX) X(_PC_PIPE_BUF)
510 X(_PC_CHOWN_RESTRICTED) X(_PC_NO_TRUNC) X(_PC_VDISABLE)
511 X(_PC_ASYNC_IO) X(_PC_PRIO_IO) X(_PC_SYNC_IO)
512 X(_PC_ALLOC_SIZE_MIN) X(_PC_FILESIZEBITS)
513 X(_PC_REC_INCR_XFER_SIZE) X(_PC_REC_MAX_XFER_SIZE)
514 X(_PC_REC_MIN_XFER_SIZE) X(_PC_REC_XFER_ALIGN)
515 X(_PC_SYMLINK_MAX) X(_PC_ACL_EXTENDED) X(_PC_ACL_PATH_MAX)
516 X(_PC_CAP_PRESENT) X(_PC_INF_PRESENT) X(_PC_MAC_PRESENT)
517 X(_PC_ACL_NFS4) X(_PC_MIN_HOLE_SIZE) XEND
518};
519
520static struct xlat rfork_flags[] = {
521 X(RFFDG) X(RFPROC) X(RFMEM) X(RFNOWAIT) X(RFCFDG) X(RFTHREAD)
522 X(RFSIGSHARE) X(RFLINUXTHPN) X(RFTSIGZMB) X(RFPPWAIT) XEND
523};
524
525static struct xlat wait_options[] = {
526 X(WNOHANG) X(WUNTRACED) X(WCONTINUED) X(WNOWAIT) X(WEXITED)
527 X(WTRAPPED) XEND
528};
529
530static struct xlat idtype_arg[] = {
531 X(P_PID) X(P_PPID) X(P_PGID) X(P_SID) X(P_CID) X(P_UID) X(P_GID)
532 X(P_ALL) X(P_LWPID) X(P_TASKID) X(P_PROJID) X(P_POOLID) X(P_JAILID)
533 X(P_CTID) X(P_CPUID) X(P_PSETID) XEND
534};
535
536static struct xlat procctl_arg[] = {
537 X(PROC_SPROTECT) XEND
538};
539
540static struct xlat umtx_ops[] = {
541 X(UMTX_OP_RESERVED0) X(UMTX_OP_RESERVED1) X(UMTX_OP_WAIT)
542 X(UMTX_OP_WAKE) X(UMTX_OP_MUTEX_TRYLOCK) X(UMTX_OP_MUTEX_LOCK)
543 X(UMTX_OP_MUTEX_UNLOCK) X(UMTX_OP_SET_CEILING) X(UMTX_OP_CV_WAIT)
544 X(UMTX_OP_CV_SIGNAL) X(UMTX_OP_CV_BROADCAST) X(UMTX_OP_WAIT_UINT)
545 X(UMTX_OP_RW_RDLOCK) X(UMTX_OP_RW_WRLOCK) X(UMTX_OP_RW_UNLOCK)
546 X(UMTX_OP_WAIT_UINT_PRIVATE) X(UMTX_OP_WAKE_PRIVATE)
547 X(UMTX_OP_MUTEX_WAIT) X(UMTX_OP_MUTEX_WAKE) X(UMTX_OP_SEM_WAIT)
548 X(UMTX_OP_SEM_WAKE) X(UMTX_OP_NWAKE_PRIVATE) X(UMTX_OP_MUTEX_WAKE2)
549 X(UMTX_OP_SEM2_WAIT) X(UMTX_OP_SEM2_WAKE)
550 XEND
551};
552
553static struct xlat at_flags[] = {
554 X(AT_EACCESS) X(AT_SYMLINK_NOFOLLOW) X(AT_SYMLINK_FOLLOW)
555 X(AT_REMOVEDIR) XEND
556};
557
558static struct xlat access_modes[] = {
559 X(R_OK) X(W_OK) X(X_OK) XEND
560};
561
562static struct xlat sysarch_ops[] = {
563#if defined(__i386__) || defined(__amd64__)
564 X(I386_GET_LDT) X(I386_SET_LDT) X(I386_GET_IOPERM) X(I386_SET_IOPERM)
565 X(I386_VM86) X(I386_GET_FSBASE) X(I386_SET_FSBASE) X(I386_GET_GSBASE)
566 X(I386_SET_GSBASE) X(I386_GET_XFPUSTATE) X(AMD64_GET_FSBASE)
567 X(AMD64_SET_FSBASE) X(AMD64_GET_GSBASE) X(AMD64_SET_GSBASE)
568 X(AMD64_GET_XFPUSTATE)
569#endif
570 XEND
571};
572
573static struct xlat linux_socketcall_ops[] = {
574 X(LINUX_SOCKET) X(LINUX_BIND) X(LINUX_CONNECT) X(LINUX_LISTEN)
575 X(LINUX_ACCEPT) X(LINUX_GETSOCKNAME) X(LINUX_GETPEERNAME)
576 X(LINUX_SOCKETPAIR) X(LINUX_SEND) X(LINUX_RECV) X(LINUX_SENDTO)
577 X(LINUX_RECVFROM) X(LINUX_SHUTDOWN) X(LINUX_SETSOCKOPT)
578 X(LINUX_GETSOCKOPT) X(LINUX_SENDMSG) X(LINUX_RECVMSG)
579 XEND
580};
581
582static struct xlat sigprocmask_ops[] = {
583 X(SIG_BLOCK) X(SIG_UNBLOCK) X(SIG_SETMASK)
584 XEND
585};
586
587#undef X
588#undef XEND
589
590/*
591 * Searches an xlat array for a value, and returns it if found. Otherwise
592 * return a string representation.
593 */
594static const char *
595lookup(struct xlat *xlat, int val, int base)
596{
597 static char tmp[16];
598
599 for (; xlat->str != NULL; xlat++)
600 if (xlat->val == val)
601 return (xlat->str);
602 switch (base) {
603 case 8:
604 sprintf(tmp, "0%o", val);
605 break;
606 case 16:
607 sprintf(tmp, "0x%x", val);
608 break;
609 case 10:
610 sprintf(tmp, "%u", val);
611 break;
612 default:
613 errx(1,"Unknown lookup base");
614 break;
615 }
616 return (tmp);
617}
618
619static const char *
620xlookup(struct xlat *xlat, int val)
621{
622
623 return (lookup(xlat, val, 16));
624}
625
626/*
627 * Searches an xlat array containing bitfield values. Remaining bits
628 * set after removing the known ones are printed at the end:
629 * IN|0x400.
630 */
631static char *
632xlookup_bits(struct xlat *xlat, int val)
633{
634 int len, rem;
635 static char str[512];
636
637 len = 0;
638 rem = val;
639 for (; xlat->str != NULL; xlat++) {
640 if ((xlat->val & rem) == xlat->val) {
641 /*
642 * Don't print the "all-bits-zero" string unless all
643 * bits are really zero.
644 */
645 if (xlat->val == 0 && val != 0)
646 continue;
647 len += sprintf(str + len, "%s|", xlat->str);
648 rem &= ~(xlat->val);
649 }
650 }
651
652 /*
653 * If we have leftover bits or didn't match anything, print
654 * the remainder.
655 */
656 if (rem || len == 0)
657 len += sprintf(str + len, "0x%x", rem);
658 if (len && str[len - 1] == '|')
659 len--;
660 str[len] = 0;
661 return (str);
662}
663
664/*
665 * If/when the list gets big, it might be desirable to do it
666 * as a hash table or binary search.
667 */
668struct syscall *
669get_syscall(const char *name)
670{
671 struct syscall *sc;
672
673 sc = syscalls;
674 if (name == NULL)
675 return (NULL);
676 while (sc->name) {
677 if (strcmp(name, sc->name) == 0)
678 return (sc);
679 sc++;
680 }
681 return (NULL);
682}
683
684/*
685 * Copy a fixed amount of bytes from the process.
686 */
687static int
688get_struct(pid_t pid, void *offset, void *buf, int len)
689{
690 struct ptrace_io_desc iorequest;
691
692 iorequest.piod_op = PIOD_READ_D;
693 iorequest.piod_offs = offset;
694 iorequest.piod_addr = buf;
695 iorequest.piod_len = len;
696 if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0)
697 return (-1);
698 return (0);
699}
700
701#define MAXSIZE 4096
702
703/*
704 * Copy a string from the process. Note that it is
705 * expected to be a C string, but if max is set, it will
706 * only get that much.
707 */
708static char *
709get_string(pid_t pid, void *addr, int max)
710{
711 struct ptrace_io_desc iorequest;
712 char *buf, *nbuf;
713 size_t offset, size, totalsize;
714
715 offset = 0;
716 if (max)
717 size = max + 1;
718 else {
719 /* Read up to the end of the current page. */
720 size = PAGE_SIZE - ((uintptr_t)addr % PAGE_SIZE);
721 if (size > MAXSIZE)
722 size = MAXSIZE;
723 }
724 totalsize = size;
725 buf = malloc(totalsize);
726 if (buf == NULL)
727 return (NULL);
728 for (;;) {
729 iorequest.piod_op = PIOD_READ_D;
730 iorequest.piod_offs = (char *)addr + offset;
731 iorequest.piod_addr = buf + offset;
732 iorequest.piod_len = size;
733 if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) {
734 free(buf);
735 return (NULL);
736 }
737 if (memchr(buf + offset, '\0', size) != NULL)
738 return (buf);
739 offset += size;
740 if (totalsize < MAXSIZE && max == 0) {
741 size = MAXSIZE - totalsize;
742 if (size > PAGE_SIZE)
743 size = PAGE_SIZE;
744 nbuf = realloc(buf, totalsize + size);
745 if (nbuf == NULL) {
746 buf[totalsize - 1] = '\0';
747 return (buf);
748 }
749 buf = nbuf;
750 totalsize += size;
751 } else {
752 buf[totalsize - 1] = '\0';
753 return (buf);
754 }
755 }
756}
757
758static char *
759strsig2(int sig)
760{
761 static char tmp[sizeof(int) * 3 + 1];
762 char *ret;
763
764 ret = strsig(sig);
765 if (ret == NULL) {
766 snprintf(tmp, sizeof(tmp), "%d", sig);
767 ret = tmp;
768 }
769 return (ret);
770}
771
772static void
773print_kevent(FILE *fp, struct kevent *ke, int input)
774{
775
776 switch (ke->filter) {
777 case EVFILT_READ:
778 case EVFILT_WRITE:
779 case EVFILT_VNODE:
780 case EVFILT_PROC:
781 case EVFILT_TIMER:
782 case EVFILT_PROCDESC:
783 fprintf(fp, "%ju", (uintmax_t)ke->ident);
784 break;
785 case EVFILT_SIGNAL:
786 fputs(strsig2(ke->ident), fp);
787 break;
788 default:
789 fprintf(fp, "%p", (void *)ke->ident);
790 }
791 fprintf(fp, ",%s,%s,", xlookup(kevent_filters, ke->filter),
792 xlookup_bits(kevent_flags, ke->flags));
793 switch (ke->filter) {
794 case EVFILT_READ:
795 case EVFILT_WRITE:
796 fputs(xlookup_bits(kevent_rdwr_fflags, ke->fflags), fp);
797 break;
798 case EVFILT_VNODE:
799 fputs(xlookup_bits(kevent_vnode_fflags, ke->fflags), fp);
800 break;
801 case EVFILT_PROC:
802 case EVFILT_PROCDESC:
803 fputs(xlookup_bits(kevent_proc_fflags, ke->fflags), fp);
804 break;
805 case EVFILT_TIMER:
806 fputs(xlookup_bits(kevent_timer_fflags, ke->fflags), fp);
807 break;
808 case EVFILT_USER: {
809 int ctrl, data;
810
811 ctrl = ke->fflags & NOTE_FFCTRLMASK;
812 data = ke->fflags & NOTE_FFLAGSMASK;
813 if (input) {
814 fputs(xlookup(kevent_user_ffctrl, ctrl), fp);
815 if (ke->fflags & NOTE_TRIGGER)
816 fputs("|NOTE_TRIGGER", fp);
817 if (data != 0)
818 fprintf(fp, "|%#x", data);
819 } else {
820 fprintf(fp, "%#x", data);
821 }
822 break;
823 }
824 default:
825 fprintf(fp, "%#x", ke->fflags);
826 }
827 fprintf(fp, ",%p,%p", (void *)ke->data, (void *)ke->udata);
828}
829
830/*
831 * Converts a syscall argument into a string. Said string is
832 * allocated via malloc(), so needs to be free()'d. sc is
833 * a pointer to the syscall description (see above); args is
834 * an array of all of the system call arguments.
835 */
836char *
837print_arg(struct syscall_args *sc, unsigned long *args, long retval,
838 struct trussinfo *trussinfo)
839{
840 FILE *fp;
841 char *tmp;
842 size_t tmplen;
843 pid_t pid;
844
845 fp = open_memstream(&tmp, &tmplen);
846 pid = trussinfo->pid;
847 switch (sc->type & ARG_MASK) {
848 case Hex:
849 fprintf(fp, "0x%x", (int)args[sc->offset]);
850 break;
851 case Octal:
852 fprintf(fp, "0%o", (int)args[sc->offset]);
853 break;
854 case Int:
855 fprintf(fp, "%d", (int)args[sc->offset]);
856 break;
857 case LongHex:
858 fprintf(fp, "0x%lx", args[sc->offset]);
859 break;
860 case Long:
861 fprintf(fp, "%ld", args[sc->offset]);
862 break;
863 case Name: {
864 /* NULL-terminated string. */
865 char *tmp2;
866
867 tmp2 = get_string(pid, (void*)args[sc->offset], 0);
868 fprintf(fp, "\"%s\"", tmp2);
869 free(tmp2);
870 break;
871 }
872 case BinString: {
873 /*
874 * Binary block of data that might have printable characters.
875 * XXX If type|OUT, assume that the length is the syscall's
876 * return value. Otherwise, assume that the length of the block
877 * is in the next syscall argument.
878 */
879 int max_string = trussinfo->strsize;
880 char tmp2[max_string + 1], *tmp3;
881 int len;
882 int truncated = 0;
883
884 if (sc->type & OUT)
885 len = retval;
886 else
887 len = args[sc->offset + 1];
888
889 /*
890 * Don't print more than max_string characters, to avoid word
891 * wrap. If we have to truncate put some ... after the string.
892 */
893 if (len > max_string) {
894 len = max_string;
895 truncated = 1;
896 }
897 if (len && get_struct(pid, (void*)args[sc->offset], &tmp2, len)
898 != -1) {
899 tmp3 = malloc(len * 4 + 1);
900 while (len) {
901 if (strvisx(tmp3, tmp2, len,
902 VIS_CSTYLE|VIS_TAB|VIS_NL) <= max_string)
903 break;
904 len--;
905 truncated = 1;
906 };
907 fprintf(fp, "\"%s\"%s", tmp3, truncated ?
908 "..." : "");
909 free(tmp3);
910 } else {
911 fprintf(fp, "0x%lx", args[sc->offset]);
912 }
913 break;
914 }
915 case StringArray: {
916 int num, size, i;
917 char *tmp2;
918 char *string;
919 char *strarray[100]; /* XXX This is ugly. */
920
921 if (get_struct(pid, (void *)args[sc->offset],
922 (void *)&strarray, sizeof(strarray)) == -1)
923 err(1, "get_struct %p", (void *)args[sc->offset]);
924 num = 0;
925 size = 0;
926
927 /* Find out how large of a buffer we'll need. */
928 while (strarray[num] != NULL) {
929 string = get_string(pid, (void*)strarray[num], 0);
930 size += strlen(string);
931 free(string);
932 num++;
933 }
934 size += 4 + (num * 4);
935 tmp = (char *)malloc(size);
936 tmp2 = tmp;
937
938 tmp2 += sprintf(tmp2, " [");
939 for (i = 0; i < num; i++) {
940 string = get_string(pid, (void*)strarray[i], 0);
941 tmp2 += sprintf(tmp2, " \"%s\"%c", string,
942 (i + 1 == num) ? ' ' : ',');
943 free(string);
944 }
945 tmp2 += sprintf(tmp2, "]");
946 break;
947 }
948#ifdef __LP64__
949 case Quad:
950 fprintf(fp, "0x%lx", args[sc->offset]);
951 break;
952#else
953 case Quad: {
954 unsigned long long ll;
955
956 ll = *(unsigned long long *)(args + sc->offset);
957 fprintf(fp, "0x%llx", ll);
958 break;
959 }
960#endif
961 case Ptr:
962 fprintf(fp, "0x%lx", args[sc->offset]);
963 break;
964 case Readlinkres: {
965 char *tmp2;
966
967 if (retval == -1)
968 break;
969 tmp2 = get_string(pid, (void*)args[sc->offset], retval);
970 fprintf(fp, "\"%s\"", tmp2);
971 free(tmp2);
972 break;
973 }
974 case Ioctl: {
975 const char *temp;
976 unsigned long cmd;
977
978 cmd = args[sc->offset];
979 temp = ioctlname(cmd);
980 if (temp)
981 fputs(temp, fp);
982 else {
983 fprintf(fp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu }",
984 cmd, cmd & IOC_OUT ? "R" : "",
985 cmd & IOC_IN ? "W" : "", IOCGROUP(cmd),
986 isprint(IOCGROUP(cmd)) ? (char)IOCGROUP(cmd) : '?',
987 cmd & 0xFF, IOCPARM_LEN(cmd));
988 }
989 break;
990 }
991 case Timespec: {
992 struct timespec ts;
993
994 if (get_struct(pid, (void *)args[sc->offset], &ts,
995 sizeof(ts)) != -1)
996 fprintf(fp, "{ %ld.%09ld }", (long)ts.tv_sec,
997 ts.tv_nsec);
998 else
999 fprintf(fp, "0x%lx", args[sc->offset]);
1000 break;
1001 }
1002 case Timespec2: {
1003 struct timespec ts[2];
1004 const char *sep;
1005 unsigned int i;
1006
1007 if (get_struct(pid, (void *)args[sc->offset], &ts, sizeof(ts))
1008 != -1) {
1009 fputs("{ ", fp);
1010 sep = "";
1011 for (i = 0; i < nitems(ts); i++) {
1012 fputs(sep, fp);
1013 sep = ", ";
1014 switch (ts[i].tv_nsec) {
1015 case UTIME_NOW:
1016 fprintf(fp, "UTIME_NOW");
1017 break;
1018 case UTIME_OMIT:
1019 fprintf(fp, "UTIME_OMIT");
1020 break;
1021 default:
1022 fprintf(fp, "%ld.%09ld",
1023 (long)ts[i].tv_sec, ts[i].tv_nsec);
1024 break;
1025 }
1026 }
1027 fputs(" }", fp);
1028 } else
1029 fprintf(fp, "0x%lx", args[sc->offset]);
1030 break;
1031 }
1032 case Timeval: {
1033 struct timeval tv;
1034
1035 if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv))
1036 != -1)
1037 fprintf(fp, "{ %ld.%06ld }", (long)tv.tv_sec,
1038 tv.tv_usec);
1039 else
1040 fprintf(fp, "0x%lx", args[sc->offset]);
1041 break;
1042 }
1043 case Timeval2: {
1044 struct timeval tv[2];
1045
1046 if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv))
1047 != -1)
1048 fprintf(fp, "{ %ld.%06ld, %ld.%06ld }",
1049 (long)tv[0].tv_sec, tv[0].tv_usec,
1050 (long)tv[1].tv_sec, tv[1].tv_usec);
1051 else
1052 fprintf(fp, "0x%lx", args[sc->offset]);
1053 break;
1054 }
1055 case Itimerval: {
1056 struct itimerval itv;
1057
1058 if (get_struct(pid, (void *)args[sc->offset], &itv,
1059 sizeof(itv)) != -1)
1060 fprintf(fp, "{ %ld.%06ld, %ld.%06ld }",
1061 (long)itv.it_interval.tv_sec,
1062 itv.it_interval.tv_usec,
1063 (long)itv.it_value.tv_sec,
1064 itv.it_value.tv_usec);
1065 else
1066 fprintf(fp, "0x%lx", args[sc->offset]);
1067 break;
1068 }
1069 case LinuxSockArgs:
1070 {
1071 struct linux_socketcall_args largs;
1072
1073 if (get_struct(pid, (void *)args[sc->offset], (void *)&largs,
1074 sizeof(largs)) != -1)
1075 fprintf(fp, "{ %s, 0x%lx }",
1076 lookup(linux_socketcall_ops, largs.what, 10),
1077 (long unsigned int)largs.args);
1078 else
1079 fprintf(fp, "0x%lx", args[sc->offset]);
1080 break;
1081 }
1082 case Pollfd: {
1083 /*
1084 * XXX: A Pollfd argument expects the /next/ syscall argument
1085 * to be the number of fds in the array. This matches the poll
1086 * syscall.
1087 */
1088 struct pollfd *pfd;
1089 int numfds = args[sc->offset + 1];
1090 size_t bytes = sizeof(struct pollfd) * numfds;
1091 int i;
1092
1093 if ((pfd = malloc(bytes)) == NULL)
1094 err(1, "Cannot malloc %zu bytes for pollfd array",
1095 bytes);
1096 if (get_struct(pid, (void *)args[sc->offset], pfd, bytes)
1097 != -1) {
1098 fputs("{", fp);
1099 for (i = 0; i < numfds; i++) {
1100 fprintf(fp, " %d/%s", pfd[i].fd,
1101 xlookup_bits(poll_flags, pfd[i].events));
1102 }
1103 fputs(" }", fp);
1104 } else {
1105 fprintf(fp, "0x%lx", args[sc->offset]);
1106 }
1107 free(pfd);
1108 break;
1109 }
1110 case Fd_set: {
1111 /*
1112 * XXX: A Fd_set argument expects the /first/ syscall argument
1113 * to be the number of fds in the array. This matches the
1114 * select syscall.
1115 */
1116 fd_set *fds;
1117 int numfds = args[0];
1118 size_t bytes = _howmany(numfds, _NFDBITS) * _NFDBITS;
1119 int i;
1120
1121 if ((fds = malloc(bytes)) == NULL)
1122 err(1, "Cannot malloc %zu bytes for fd_set array",
1123 bytes);
1124 if (get_struct(pid, (void *)args[sc->offset], fds, bytes)
1125 != -1) {
1126 fputs("{", fp);
1127 for (i = 0; i < numfds; i++) {
1128 if (FD_ISSET(i, fds))
1129 fprintf(fp, " %d", i);
1130 }
1131 fputs(" }", fp);
1132 } else
1133 fprintf(fp, "0x%lx", args[sc->offset]);
1134 free(fds);
1135 break;
1136 }
1137 case Signal:
1138 fputs(strsig2(args[sc->offset]), fp);
1139 break;
1140 case Sigset: {
1141 long sig;
1142 sigset_t ss;
1143 int i, first;
1144
1145 sig = args[sc->offset];
1146 if (get_struct(pid, (void *)args[sc->offset], (void *)&ss,
1147 sizeof(ss)) == -1) {
1148 fprintf(fp, "0x%lx", args[sc->offset]);
1149 break;
1150 }
1151 fputs("{ ", fp);
1152 first = 1;
1153 for (i = 1; i < sys_nsig; i++) {
1154 if (sigismember(&ss, i)) {
1155 fprintf(fp, "%s%s", !first ? "|" : "",
1156 strsig(i));
1157 first = 0;
1158 }
1159 }
1160 if (!first)
1161 fputc(' ', fp);
1162 fputc('}', fp);
1163 break;
1164 }
1165 case Sigprocmask: {
1166 fputs(xlookup(sigprocmask_ops, args[sc->offset]), fp);
1167 break;
1168 }
1169 case Fcntlflag: {
1170 /* XXX: Output depends on the value of the previous argument. */
1171 switch (args[sc->offset - 1]) {
1172 case F_SETFD:
1173 fputs(xlookup_bits(fcntlfd_arg, args[sc->offset]), fp);
1174 break;
1175 case F_SETFL:
1176 fputs(xlookup_bits(fcntlfl_arg, args[sc->offset]), fp);
1177 break;
1178 case F_GETFD:
1179 case F_GETFL:
1180 case F_GETOWN:
1181 break;
1182 default:
1183 fprintf(fp, "0x%lx", args[sc->offset]);
1184 break;
1185 }
1186 break;
1187 }
1188 case Open:
1189 fputs(xlookup_bits(open_flags, args[sc->offset]), fp);
1190 break;
1191 case Fcntl:
1192 fputs(xlookup(fcntl_arg, args[sc->offset]), fp);
1193 break;
1194 case Mprot:
1195 fputs(xlookup_bits(mprot_flags, args[sc->offset]), fp);
1196 break;
1197 case Mmapflags: {
1198 int align, flags;
1199
1200 /*
1201 * MAP_ALIGNED can't be handled by xlookup_bits(), so
1202 * generate that string manually and prepend it to the
1203 * string from xlookup_bits(). Have to be careful to
1204 * avoid outputting MAP_ALIGNED|0 if MAP_ALIGNED is
1205 * the only flag.
1206 */
1207 flags = args[sc->offset] & ~MAP_ALIGNMENT_MASK;
1208 align = args[sc->offset] & MAP_ALIGNMENT_MASK;
1209 if (align != 0) {
1210 if (align == MAP_ALIGNED_SUPER)
1211 fputs("MAP_ALIGNED_SUPER", fp);
1212 else
1213 fprintf(fp, "MAP_ALIGNED(%d)",
1214 align >> MAP_ALIGNMENT_SHIFT);
1215 if (flags == 0)
1216 break;
1217 fputc('|', fp);
1218 }
1219 fputs(xlookup_bits(mmap_flags, flags), fp);
1220 break;
1221 }
1222 case Whence:
1223 fputs(xlookup(whence_arg, args[sc->offset]), fp);
1224 break;
1225 case Sockdomain:
1226 fputs(xlookup(sockdomain_arg, args[sc->offset]), fp);
1227 break;
1228 case Socktype: {
1229 int type, flags;
1230
1231 flags = args[sc->offset] & (SOCK_CLOEXEC | SOCK_NONBLOCK);
1232 type = args[sc->offset] & ~flags;
1233 fputs(xlookup(socktype_arg, type), fp);
1234 if (flags & SOCK_CLOEXEC)
1235 fprintf(fp, "|SOCK_CLOEXEC");
1236 if (flags & SOCK_NONBLOCK)
1237 fprintf(fp, "|SOCK_NONBLOCK");
1238 break;
1239 }
1240 case Shutdown:
1241 fputs(xlookup(shutdown_arg, args[sc->offset]), fp);
1242 break;
1243 case Resource:
1244 fputs(xlookup(resource_arg, args[sc->offset]), fp);
1245 break;
1246 case Pathconf:
1247 fputs(xlookup(pathconf_arg, args[sc->offset]), fp);
1248 break;
1249 case Rforkflags:
1250 fputs(xlookup_bits(rfork_flags, args[sc->offset]), fp);
1251 break;
1252 case Sockaddr: {
1253 struct sockaddr_storage ss;
1254 char addr[64];
1255 struct sockaddr_in *lsin;
1256 struct sockaddr_in6 *lsin6;
1257 struct sockaddr_un *sun;
1258 struct sockaddr *sa;
1259 u_char *q;
1260
1261 if (args[sc->offset] == 0) {
1262 fputs("NULL", fp);
1263 break;
1264 }
1265
1266 /* yuck: get ss_len */
1267 if (get_struct(pid, (void *)args[sc->offset], (void *)&ss,
1268 sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1) {
1269 fprintf(fp, "0x%lx", args[sc->offset]);
1270 break;
1271 }
1272
1273 /*
1274 * If ss_len is 0, then try to guess from the sockaddr type.
1275 * AF_UNIX may be initialized incorrectly, so always frob
1276 * it by using the "right" size.
1277 */
1278 if (ss.ss_len == 0 || ss.ss_family == AF_UNIX) {
1279 switch (ss.ss_family) {
1280 case AF_INET:
1281 ss.ss_len = sizeof(*lsin);
1282 break;
1283 case AF_INET6:
1284 ss.ss_len = sizeof(*lsin6);
1285 break;
1286 case AF_UNIX:
1287 ss.ss_len = sizeof(*sun);
1288 break;
1289 default:
1290 break;
1291 }
1292 }
1293 if (ss.ss_len != 0 &&
1294 get_struct(pid, (void *)args[sc->offset], (void *)&ss,
1295 ss.ss_len) == -1) {
1296 fprintf(fp, "0x%lx", args[sc->offset]);
1297 break;
1298 }
1299
1300 switch (ss.ss_family) {
1301 case AF_INET:
1302 lsin = (struct sockaddr_in *)&ss;
1303 inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof(addr));
1304 fprintf(fp, "{ AF_INET %s:%d }", addr,
1305 htons(lsin->sin_port));
1306 break;
1307 case AF_INET6:
1308 lsin6 = (struct sockaddr_in6 *)&ss;
1309 inet_ntop(AF_INET6, &lsin6->sin6_addr, addr,
1310 sizeof(addr));
1311 fprintf(fp, "{ AF_INET6 [%s]:%d }", addr,
1312 htons(lsin6->sin6_port));
1313 break;
1314 case AF_UNIX:
1315 sun = (struct sockaddr_un *)&ss;
1316 fprintf(fp, "{ AF_UNIX \"%s\" }", sun->sun_path);
1317 break;
1318 default:
1319 sa = (struct sockaddr *)&ss;
1320 fprintf(fp,
1321 "{ sa_len = %d, sa_family = %d, sa_data = {",
1322 (int)sa->sa_len, (int)sa->sa_family);
1323 for (q = (u_char *)sa->sa_data;
1324 q < (u_char *)sa + sa->sa_len; q++)
1325 fprintf(fp, "%s 0x%02x",
1326 q == (u_char *)sa->sa_data ? "" : ",",
1327 *q);
1328 fputs(" } }", fp);
1329 }
1330 break;
1331 }
1332 case Sigaction: {
1333 struct sigaction sa;
1334
1335 if (get_struct(pid, (void *)args[sc->offset], &sa, sizeof(sa))
1336 != -1) {
1337 fputs("{ ", fp);
1338 if (sa.sa_handler == SIG_DFL)
1339 fputs("SIG_DFL", fp);
1340 else if (sa.sa_handler == SIG_IGN)
1341 fputs("SIG_IGN", fp);
1342 else
1343 fprintf(fp, "%p", sa.sa_handler);
1344 fprintf(fp, " %s ss_t }",
1345 xlookup_bits(sigaction_flags, sa.sa_flags));
1346 } else
1347 fprintf(fp, "0x%lx", args[sc->offset]);
1348 break;
1349 }
1350 case Kevent: {
1351 /*
1352 * XXX XXX: The size of the array is determined by either the
1353 * next syscall argument, or by the syscall return value,
1354 * depending on which argument number we are. This matches the
1355 * kevent syscall, but luckily that's the only syscall that uses
1356 * them.
1357 */
1358 struct kevent *ke;
1359 int numevents = -1;
1360 size_t bytes;
1361 int i;
1362
1363 if (sc->offset == 1)
1364 numevents = args[sc->offset+1];
1365 else if (sc->offset == 3 && retval != -1)
1366 numevents = retval;
1367
1368 if (numevents >= 0) {
1369 bytes = sizeof(struct kevent) * numevents;
1370 if ((ke = malloc(bytes)) == NULL)
1371 err(1,
1372 "Cannot malloc %zu bytes for kevent array",
1373 bytes);
1374 } else
1375 ke = NULL;
1376 if (numevents >= 0 && get_struct(pid, (void *)args[sc->offset],
1377 ke, bytes) != -1) {
1378 fputc('{', fp);
1379 for (i = 0; i < numevents; i++) {
1380 fputc(' ', fp);
1381 print_kevent(fp, &ke[i], sc->offset == 1);
1382 }
1383 fputs(" }", fp);
1384 } else {
1385 fprintf(fp, "0x%lx", args[sc->offset]);
1386 }
1387 free(ke);
1388 break;
1389 }
1390 case Stat: {
1391 struct stat st;
1392
1393 if (get_struct(pid, (void *)args[sc->offset], &st, sizeof(st))
1394 != -1) {
1395 char mode[12];
1396
1397 strmode(st.st_mode, mode);
1398 fprintf(fp,
1399 "{ mode=%s,inode=%jd,size=%jd,blksize=%ld }", mode,
1400 (intmax_t)st.st_ino, (intmax_t)st.st_size,
1401 (long)st.st_blksize);
1402 } else {
1403 fprintf(fp, "0x%lx", args[sc->offset]);
1404 }
1405 break;
1406 }
1407 case Rusage: {
1408 struct rusage ru;
1409
1410 if (get_struct(pid, (void *)args[sc->offset], &ru, sizeof(ru))
1411 != -1) {
1412 fprintf(fp,
1413 "{ u=%ld.%06ld,s=%ld.%06ld,in=%ld,out=%ld }",
1414 (long)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec,
1415 (long)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec,
1416 ru.ru_inblock, ru.ru_oublock);
1417 } else
1418 fprintf(fp, "0x%lx", args[sc->offset]);
1419 break;
1420 }
1421 case Rlimit: {
1422 struct rlimit rl;
1423
1424 if (get_struct(pid, (void *)args[sc->offset], &rl, sizeof(rl))
1425 != -1) {
1426 fprintf(fp, "{ cur=%ju,max=%ju }",
1427 rl.rlim_cur, rl.rlim_max);
1428 } else
1429 fprintf(fp, "0x%lx", args[sc->offset]);
1430 break;
1431 }
1432 case ExitStatus: {
1433 int status;
1434
1435 if (get_struct(pid, (void *)args[sc->offset], &status,
1436 sizeof(status)) != -1) {
1437 fputs("{ ", fp);
1438 if (WIFCONTINUED(status))
1439 fputs("CONTINUED", fp);
1440 else if (WIFEXITED(status))
1441 fprintf(fp, "EXITED,val=%d",
1442 WEXITSTATUS(status));
1443 else if (WIFSIGNALED(status))
1444 fprintf(fp, "SIGNALED,sig=%s%s",
1445 strsig2(WTERMSIG(status)),
1446 WCOREDUMP(status) ? ",cored" : "");
1447 else
1448 fprintf(fp, "STOPPED,sig=%s",
1449 strsig2(WTERMSIG(status)));
1450 fputs(" }", fp);
1451 } else
1452 fprintf(fp, "0x%lx", args[sc->offset]);
1453 break;
1454 }
1455 case Waitoptions:
1456 fputs(xlookup_bits(wait_options, args[sc->offset]), fp);
1457 break;
1458 case Idtype:
1459 fputs(xlookup(idtype_arg, args[sc->offset]), fp);
1460 break;
1461 case Procctl:
1462 fputs(xlookup(procctl_arg, args[sc->offset]), fp);
1463 break;
1464 case Umtxop:
1465 fputs(xlookup(umtx_ops, args[sc->offset]), fp);
1466 break;
1467 case Atfd:
1468 if ((int)args[sc->offset] == AT_FDCWD)
1469 fputs("AT_FDCWD", fp);
1470 else
1471 fprintf(fp, "%d", (int)args[sc->offset]);
1472 break;
1473 case Atflags:
1474 fputs(xlookup_bits(at_flags, args[sc->offset]), fp);
1475 break;
1476 case Accessmode:
1477 if (args[sc->offset] == F_OK)
1478 fputs("F_OK", fp);
1479 else
1480 fputs(xlookup_bits(access_modes, args[sc->offset]), fp);
1481 break;
1482 case Sysarch:
1483 fputs(xlookup(sysarch_ops, args[sc->offset]), fp);
1484 break;
1485 default:
1486 errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK);
1487 }
1488 fclose(fp);
1489 return (tmp);
1490}
1491
1492/*
1493 * Print (to outfile) the system call and its arguments. Note that
1494 * nargs is the number of arguments (not the number of words; this is
1495 * potentially confusing, I know).
1496 */
1497void
1498print_syscall(struct trussinfo *trussinfo, const char *name, int nargs,
1499 char **s_args)
1500{
1501 struct timespec timediff;
1502 int i, len;
1503
1504 len = 0;
1505 if (trussinfo->flags & FOLLOWFORKS)
1506 len += fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid);
1507
1508 if (name != NULL && (strcmp(name, "execve") == 0 ||
1509 strcmp(name, "exit") == 0)) {
1510 clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after);
1511 }
1512
1513 if (trussinfo->flags & ABSOLUTETIMESTAMPS) {
1514 timespecsubt(&trussinfo->curthread->after,
1515 &trussinfo->start_time, &timediff);
1516 len += fprintf(trussinfo->outfile, "%ld.%09ld ",
1517 (long)timediff.tv_sec, timediff.tv_nsec);
1518 }
1519
1520 if (trussinfo->flags & RELATIVETIMESTAMPS) {
1521 timespecsubt(&trussinfo->curthread->after,
1522 &trussinfo->curthread->before, &timediff);
1523 len += fprintf(trussinfo->outfile, "%ld.%09ld ",
1524 (long)timediff.tv_sec, timediff.tv_nsec);
1525 }
1526
1527 len += fprintf(trussinfo->outfile, "%s(", name);
1528
1529 for (i = 0; i < nargs; i++) {
1530 if (s_args[i])
1531 len += fprintf(trussinfo->outfile, "%s", s_args[i]);
1532 else
1533 len += fprintf(trussinfo->outfile,
1534 "<missing argument>");
1535 len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ?
1536 "," : "");
1537 }
1538 len += fprintf(trussinfo->outfile, ")");
1539 for (i = 0; i < 6 - (len / 8); i++)
1540 fprintf(trussinfo->outfile, "\t");
1541}
1542
1543void
1544print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs,
1545 char **s_args, int errorp, long retval, struct syscall *sc)
1546{
1547 struct timespec timediff;
1548
1549 if (trussinfo->flags & COUNTONLY) {
1550 if (!sc)
1551 return;
1552 clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after);
1553 timespecsubt(&trussinfo->curthread->after,
1554 &trussinfo->curthread->before, &timediff);
1555 timespecadd(&sc->time, &timediff, &sc->time);
1556 sc->ncalls++;
1557 if (errorp)
1558 sc->nerror++;
1559 return;
1560 }
1561
1562 print_syscall(trussinfo, name, nargs, s_args);
1563 fflush(trussinfo->outfile);
1564 if (errorp)
1565 fprintf(trussinfo->outfile, " ERR#%ld '%s'\n", retval,
1566 strerror(retval));
1567 else {
1568 /*
1569 * Because pipe(2) has a special assembly glue to provide the
1570 * libc API, we have to adjust retval.
1571 */
1572 if (name != NULL && strcmp(name, "pipe") == 0)
1573 retval = 0;
1574 fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval, retval);
1575 }
1576}
1577
1578void
1579print_summary(struct trussinfo *trussinfo)
1580{
1581 struct timespec total = {0, 0};
1582 struct syscall *sc;
1583 int ncall, nerror;
1584
1585 fprintf(trussinfo->outfile, "%-20s%15s%8s%8s\n",
1586 "syscall", "seconds", "calls", "errors");
1587 ncall = nerror = 0;
1588 for (sc = syscalls; sc->name != NULL; sc++)
1589 if (sc->ncalls) {
1590 fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n",
1591 sc->name, (intmax_t)sc->time.tv_sec,
1592 sc->time.tv_nsec, sc->ncalls, sc->nerror);
1593 timespecadd(&total, &sc->time, &total);
1594 ncall += sc->ncalls;
1595 nerror += sc->nerror;
1596 }
1597 fprintf(trussinfo->outfile, "%20s%15s%8s%8s\n",
1598 "", "-------------", "-------", "-------");
1599 fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n",
1600 "", (intmax_t)total.tv_sec, total.tv_nsec, ncall, nerror);
1601}