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